Skip to content

Code Examples

Four real-shaped examples: a natural-language extraction, a schema-constrained extraction, a markdown read, and branching on the response like a production caller would.

1. Natural Language Extraction

Describe what you want in plain English. No schema required.

curl
curl -X POST https://contrie.com/api/v1/scrape \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CONTRIE_API_KEY" \
  -d '{
    "url": "https://example.com/products/wireless-headphones",
    "extract": "Get the product name, price, rating, and whether it is in stock"
  }'
200 OK
{
  "success": true,
  "data": {
    "name": "Wireless Noise-Cancelling Headphones",
    "price": 79.99,
    "rating": 4.5,
    "inStock": true
  },
  "metadata": {
    "extractionMethod": "ai",
    "model": "minimax/minimax-m3:free",
    "qualityScore": 88,
    "grounding": 0.9,
    "escalations": 0,
    "costUsd": 0,
    "trace": [
      { "stage": "heuristic", "detail": "found Product JSON-LD" },
      { "stage": "classify", "detail": "simple" },
      { "stage": "route", "detail": "hedged", "tier": "free" },
      { "stage": "extract", "model": "minimax/minimax-m3:free", "detail": "2140 in / 60 out", "ms": 1340 },
      { "stage": "validate", "detail": "quality 88, grounding 0.9", "quality": 88 }
    ]
  }
}

2. JSON Schema Extraction

Pass a schema when downstream code depends on the exact shape. Validation failures cost points against qualityScore.

Node.js
const response = await fetch("https://contrie.com/api/v1/scrape", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.CONTRIE_API_KEY}`,
  },
  body: JSON.stringify({
    url: "https://example.com/products",
    extract: "Get every product on the page",
    schema: {
      type: "object",
      properties: {
        products: {
          type: "array",
          items: {
            type: "object",
            properties: {
              name: { type: "string" },
              price: { type: "number" },
              inStock: { type: "boolean" },
            },
            required: ["name", "price"],
          },
        },
      },
      required: ["products"],
    },
  }),
});

const { data, metadata } = await response.json();
// data.products is guaranteed to match the schema when qualityScore >= 70
200 OK
{
  "success": true,
  "data": {
    "products": [
      { "name": "Wireless Headphones", "price": 79.99, "inStock": true },
      { "name": "USB-C Cable", "price": 12.5, "inStock": false }
    ]
  },
  "metadata": {
    "extractionMethod": "ai",
    "model": "deepseek/deepseek-v4-pro-0813",
    "qualityScore": 96,
    "grounding": 1,
    "escalations": 0,
    "costUsd": 0.0021,
    "trace": [
      { "stage": "heuristic", "detail": "no structured data found" },
      { "stage": "classify", "detail": "simple" },
      { "stage": "route", "detail": "budget tier, schema present" },
      { "stage": "extract", "model": "deepseek/deepseek-v4-pro-0813", "detail": "4830 in / 210 out", "ms": 2010 },
      { "stage": "validate", "detail": "schema valid, quality 96", "quality": 96 }
    ]
  }
}

3. Read a Page as Markdown

format: "markdown" skips the model entirely — zero cost, and data is null.

curl
curl -X POST https://contrie.com/api/v1/scrape \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $CONTRIE_API_KEY" \
  -d '{
    "url": "https://news.ycombinator.com",
    "format": "markdown"
  }'
200 OK
{
  "success": true,
  "data": null,
  "markdown": "# Hacker News\n\n1. [Show HN: I built an open-source AI code editor](https://...) (847 points)\n2. ...",
  "metadata": {
    "extractionMethod": "markdown",
    "qualityScore": 100,
    "costUsd": 0,
    "trace": [
      { "stage": "markdown", "detail": "page reduced to 6.8KB markdown" }
    ]
  }
}

The MCP contrie_read tool wraps the same path — see MCP.

4. Branching on the Response

A 200 status doesn't mean the data is good, and a caught error doesn't always mean give up. This is roughly what a production caller should do:

JavaScript
async function extract(url, prompt) {
  const res = await fetch("https://contrie.com/api/v1/scrape", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.CONTRIE_API_KEY}`,
    },
    body: JSON.stringify({ url, extract: prompt }),
  });
  const body = await res.json();

  if (!res.ok) {
    if (body.error?.code === "RENDER_REQUIRED") {
      // JS-only page — render it yourself, then call /api/v1/extract
      return { needsRender: true };
    }
    throw new Error(`${body.error.code}: ${body.error.message}`);
  }

  const { data, metadata } = body;
  const lowQuality = metadata.qualityScore < 70;
  const ungrounded = metadata.grounding !== undefined && metadata.grounding < 0.8;

  if (lowQuality || ungrounded) {
    // One retry with a narrower ask, not a blind repeat
    const retryRes = await fetch("https://contrie.com/api/v1/scrape", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.CONTRIE_API_KEY}`,
      },
      body: JSON.stringify({ url, extract: `Just the: ${prompt}` }),
    });
    return retryRes.json();
  }

  return body;
}