---
name: contrie-web-extract
description: Extract validated JSON or clean markdown from a single public web page using Contrie's hosted API or MCP server, with a quality score and grounding ratio to decide whether to trust or retry the result.
---

# Contrie web extraction

Contrie turns one URL into structured data or markdown. It routes each page
through the cheapest capable model in a 17-model fleet, validates the
output, and escalates (max 2 tiers) if quality falls short of 70/100. Every
response carries a `qualityScore`, a `grounding` ratio (fraction of
extracted values verified verbatim against the page text), the full
escalation `trace`, and `costUsd`.

## When to use this skill

- You need structured JSON from a page and can describe the fields in
  natural language or a JSON Schema.
- You need the readable content of a page as clean markdown, cheaply.
- You want to know *how confident* the extraction is before acting on it
  (quality score, grounding ratio) rather than trusting raw text.

## When NOT to use this skill

- Crawling a site or following links — Contrie extracts one page per call.
- Searching the web — there is no search endpoint.
- Multi-step browser tasks (clicking, filling forms, logging in) — Contrie
  does not drive a browser session.
- Pages behind a login wall — no session/cookie support.
- JS-only pages that render nothing server-side — the API returns
  `RENDER_REQUIRED` instead of attempting to render; see "Errors" below.

## Setup

Get a key at https://contrie.com/dashboard/keys (human sign-up via Clerk;
skip this for the sample URLs below). Store it as an environment variable,
never inline in a prompt or URL:

```bash
export CONTRIE_API_KEY=ck_live_...
```

### MCP (hosted, Streamable HTTP)

```json
{
  "mcpServers": {
    "contrie": {
      "url": "https://contrie.com/mcp",
      "headers": { "Authorization": "Bearer ${CONTRIE_API_KEY}" }
    }
  }
}
```

Tools exposed: `contrie_extract { url, extract?, schema? }` returns JSON.
`contrie_read { url }` returns markdown. Without a key, only the sample URLs
below succeed.

### MCP (local stdio, from a clone of the repo)

```bash
CONTRIE_API_KEY=ck_live_... npx -y tsx packages/mcp/src/server.ts
```

### curl fallback (no MCP client available)

```bash
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", "extract": "the page title and main heading"}'
```

Sample URLs work without a key (useful for a dry run before wiring up
auth): `https://news.ycombinator.com`, `https://example.com`,
`https://www.producthunt.com`.

## Decision procedure

1. **Reading vs. structuring.** If the goal is to read or summarize a page,
   use `format: "markdown"` — it costs no model spend and is the fastest
   path. If downstream code depends on a specific shape, use `schema`
   instead of `extract` so the response is guaranteed to match your fields.
2. **Natural language vs. schema.** Prefer `extract` (a plain-English
   description) for exploratory or one-off pulls. Prefer `schema` (JSON
   Schema: `object`/`array`/`string`/`number`/`integer`/`boolean`,
   `properties`, `required`, `items`, `enum`, `description`) whenever the
   caller parses the result programmatically.
3. **Trusting the result.** Check `metadata.qualityScore` and
   `metadata.grounding` before using `data`:
   - `qualityScore >= 70` and `grounding >= 0.8`: trust the result.
   - Below that: the page was ambiguous, sparse, or the request too broad.
     Retry once with a narrower `extract` (name the exact fields) or a
     stricter `schema` rather than repeating the same call.
4. **RENDER_REQUIRED.** Do not retry as-is — the page needs a browser to
   render content Contrie cannot fetch server-side. Surface this to the
   caller/user instead of looping.
5. **Cost.** `costUsd` is the actual model spend for that call. Free-tier
   models in the fleet cost `0` but are slower and escalate more often on
   hard pages; this is a latency/quality tradeoff, not a correctness one.

## Request/response examples

### Natural language extraction

```bash
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",
    "extract": "Top 5 stories with title, url, and points"
  }'
```

```json
{
  "success": true,
  "data": {
    "stories": [
      { "rank": 1, "title": "...", "url": "https://...", "points": 512 }
    ]
  },
  "metadata": {
    "url": "https://news.ycombinator.com",
    "extractionMethod": "ai",
    "model": "meta-llama/llama-3.3-70b-instruct:free",
    "qualityScore": 92,
    "grounding": 0.97,
    "escalations": 0,
    "latencyMs": 2340,
    "costUsd": 0,
    "trace": [
      { "stage": "heuristic", "detail": "no JSON-LD found" },
      { "stage": "classify", "detail": "simple" },
      { "stage": "route", "detail": "tier=free", "model": "meta-llama/llama-3.3-70b-instruct:free" },
      { "stage": "extract", "detail": "ok", "ms": 1800 },
      { "stage": "validate", "detail": "passed threshold", "quality": 92 }
    ],
    "credits": 1
  }
}
```

### Schema-constrained extraction

```json
{
  "url": "https://example.com/product/42",
  "schema": {
    "type": "object",
    "properties": {
      "name": { "type": "string" },
      "price": { "type": "number" },
      "inStock": { "type": "boolean" }
    },
    "required": ["name", "price"]
  }
}
```

### Markdown (zero model cost)

```json
{ "url": "https://example.com", "format": "markdown" }
```

```json
{
  "success": true,
  "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples...",
  "metadata": {
    "url": "https://example.com",
    "extractionMethod": "markdown",
    "qualityScore": 100,
    "escalations": 0,
    "latencyMs": 210,
    "costUsd": 0,
    "trace": [ { "stage": "markdown", "detail": "converted from DOM" } ],
    "credits": 1
  }
}
```

### Error (render required)

```json
{
  "success": false,
  "error": {
    "code": "RENDER_REQUIRED",
    "message": "This page requires JavaScript rendering; Contrie's hosted API does not run a browser."
  }
}
```

## Errors

`{ "success": false, "error": { "code": string, "message": string } }`.

| Code | HTTP | Meaning |
|---|---|---|
| `INVALID_REQUEST` | 400 | Malformed body |
| `INVALID_URL` | 400 | Not a valid http/https URL |
| `UNAUTHORIZED` | 401 | Missing/invalid API key |
| `FETCH_FAILED` | 422 | Target unreachable |
| `RENDER_REQUIRED` | 422 | Page needs a browser; do not retry as-is |
| `RATE_LIMITED` | 429 | Per-key rate limit hit; see `Retry-After` header |
| `PAGE_LIMIT_REACHED` | 429 | Monthly quota exhausted |
| `SERVICE_UNAVAILABLE` | 503 | Transient; retry with backoff |
| `EXTRACTION_FAILED` | 500 | Pipeline exhausted all escalations |

## Rate limits

10 requests/min per key. Monthly quota: 1,000 credits on the free plan. Credits are charged only for answers: a markdown read costs 1, a passing extraction costs 1 (free/budget model), 3 (standard/vision/premium) or 10 (frontier); results under 70, RENDER_REQUIRED, fetch failures and MODELS_UNAVAILABLE cost 0. The charge is returned as metadata.credits. Free during beta; see https://contrie.com/pricing.
SSRF guard blocks private/internal addresses, including through redirects —
requests to internal network ranges fail regardless of quota.
