Crawl4AI / docs

API reference

One key, plain JSON, one fast endpoint. Turn any URL into clean Markdown, search the web, or pull typed JSON out of a page. Every call is authenticated with your API key and returns in one round trip.

Base URL https://gate.crawl4ai.com  ·  Auth header Authorization: Bearer sk_live_...  ·  get a key

ScrapeSearchAnswerExtract BatchBulk jobsRecipesMCP

POST/scrape

Fetch a page and return clean Markdown (and/or HTML). Handles JS-heavy pages and bot walls automatically — you don't pick an engine.

curl
curl https://gate.crawl4ai.com/scrape \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "format": "both",
    "proxy": "residential",
    "country": "us",
    "parse": { "links": true, "media": true, "metadata": true, "tables": true }
  }'

Body

FieldTypeDescription
urlreqstringThe page to scrape. Must be a public http/https URL.
formatstringboth (default) · md · html — what content comes back.
proxystringnone (default) · isp · residential — which exit network to render through, for sites that block datacenters.
countrystringTwo-letter exit country (e.g. us, sg). Applies with isp/residential.
parsebool | objectAlso return structured page data. true for all, or pick: {"links":true,"media":true,"metadata":true,"tables":true}.

Web search, browser-free, results ranked and cleaned. Add rich=1 to also get the on-page extras — follow-up questions, related queries, an entity card and more (see below). For a direct answer, use /answer.

curl
curl "https://gate.crawl4ai.com/search?q=rust+web+crawler" \
  -H "Authorization: Bearer sk_live_..."

Query

ParamTypeDescription
qreqstringThe search query (max 512 chars).
rich0 | 1Default 0 (ranked links only). 1 adds a rich block: a direct answer when one exists, follow-up questions, related queries, an entity card, videos, news and more. Slightly slower; best for questions.

Rich response rich=1

Every key below is optional — each appears only when that block is on the page. For a direct answer to a question, use /answer.

rich block
{
  "results": [ … ranked links, same as always … ],
  "rich": {
    "follow_up_questions": [ "Why is the sky blue at sunset?" ],
    "related_queries":    [ "why is the ocean blue" ],
    "entity":  { "title": "Apple Inc", "subtitle": "NASDAQ: AAPL" },
    "videos": [ { "title": "…", "url": "https://…" } ],
    "news":   [ { "title": "…", "url": "https://…" } ],
    "discussions":  [ … ],
    "did_you_mean": "corrected spelling"
  }
}

GET/answer experimental

Ask a question, get a direct answer. Some questions won't have one yet — then answered is false (use /search for links). Add deep=0 for a direct answer only when one is readily available (no page reading). Experimental: the shape may change as we improve it.

curl
curl "https://gate.crawl4ai.com/answer?q=why+is+the+sky+blue" \
  -H "Authorization: Bearer sk_live_..."
response
{
  "answered": true,
  "answer": {
    "kind": "generated",
    "text": "The sky is blue because Earth's atmosphere scatters sunlight…",
    "sources": [ { "title": "NASA", "url": "https://…" } ]
  },
  "experimental": true
}

POST/extract

Pull structured, typed data out of a page with an instruction and/or a JSON schema. Give a URL (we fetch it) or your own content.

curl
curl https://gate.crawl4ai.com/extract \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://news.ycombinator.com",
    "instruction": "the top stories on the front page",
    "schema": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "title":  { "type": "string"  },
          "points": { "type": "integer" },
          "url":    { "type": "string"  }
        },
        "required": ["title", "points"]
      }
    },
    "example": [
      { "title": "Show HN: My project", "points": 128, "url": "https://..." }
    ]
  }'

Body

FieldTypeDescription
urlstringPage to read. We fetch it for you.
contentstringYour own text/markdown/html to extract from, instead of a URL.
instructionstringPlain-English description of what to pull out.
schemaobjectJSON schema each returned record must match — gives you typed, predictable output.
exampleobjectA sample of the shape you want (structure, not values).
Give a URL or content (plus an instruction and/or schema). Long pages are chunked and re-assembled automatically.

POST/scrape/batch

Scrape many URLs in one call — up to 50 — and stream a result per line as each finishes.

# -N streams each result line as it lands
curl -N https://gate.crawl4ai.com/scrape/batch \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://a.com", "https://b.com"], "format": "md"}'

Body

FieldTypeDescription
urlsreqstring[]The URLs to scrape (max 50 per call).
Any /scrape field (format, proxy, country, parse) applies to every URL.
Response is application/x-ndjson — one JSON line per URL as it completes.

POST/scrape/jobs

For big lists — up to 10,000 URLs. Submit once, get a job id, then poll for results while it drains in the background.

# 1. submit -> job id
JOB=$(curl -s https://gate.crawl4ai.com/scrape/jobs \
  -H "Authorization: Bearer sk_live_..." -H "Content-Type: application/json" \
  -d '{"urls": ["https://a.com", "https://b.com"]}' | jq -r .job_id)

# 2. poll until done
until [ "$(curl -s https://gate.crawl4ai.com/scrape/jobs/$JOB \
       -H "Authorization: Bearer sk_live_..." | jq -r .status)" = "done" ]; \
  do sleep 2; done

# 3. fetch results (NDJSON, one line per URL)
curl -s https://gate.crawl4ai.com/scrape/jobs/$JOB/results \
  -H "Authorization: Bearer sk_live_..."

Endpoints

RouteDoes
POST /scrape/jobsSubmit urls (max 10,000) + any /scrape field. Returns a job_id.
GET /scrape/jobs/{id}Status + counts (pending / done / error). Add ?full=1 for per-URL detail.
GET /scrape/jobs/{id}/resultsStreamed results, paged with ?after=N (500 per page).
POST /scrape/jobs/{id}/retryRe-run just the failed URLs.

POST/recipes/{name}

Ready-made scrapers. Pick a recipe from the catalog, send its inputs, get rows back. Browse and try them at /recipes/.

curl
curl https://gate.crawl4ai.com/recipes/hn-hiring \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "thread": 49522897, "keyword": "Rust" }'

Body

FieldTypeDescription
<input>string · int · bool · dateOne field per recipe input, as the catalog lists them. A missing input takes the recipe's default; a required one without a default fails with bad_input.
bypass_cacheboolRun again even when a fresh result is cached (see below).

Response

json
{
  "recipe": "hn-hiring", "version": 1,
  "rows": [ { "company": "Senzing", "author": "samlk", "text": "Senzing | Platform Engineer | Remote (USA) ..." } ],
  "usage": {
    "units": 1, "boxes": ["crawl"], "cache": "miss", "engine": "recipe",
    "lines": [ { "call": "scrape", "url": "https://news.ycombinator.com/item?id=49522897", "units": 1, "cache": "archive", "engine": "archive" } ]
  },
  "meta": { "pages_fetched": 1, "from_cache": false, "elapsed_ms": 812, "llm_calls": 0, "warnings": [] }
}
FieldDescription
rowsOne object per row, exactly the recipe's output fields, in order. A field the page did not have is null.
usageWhat this run deducted, the same object every endpoint returns: units, llm_tokens (only when an LLM ran), boxes, remaining_before on capped tiers. lines lists every inner call in order: each page (scrape, its url) and each LLM call (extract, its step) with its own units and tokens. Each line is one receipt in your usage history.
metapages_fetched, llm_calls, elapsed_ms, from_cache, and warnings (fields that came back empty, unmatched joins, a module's log lines).

The result cache

Every recipe declares fresh_for_s. A second call with the same inputs inside that window is served from the cache: usage.cache is "hit", usage.units is 0, meta.from_cache is true. Send "bypass_cache": true to run again. Recipes with a secret input are never cached.

Your session

Some recipes read a site as you: they take a session input, your own login cookies for that site as one Cookie header line. The Crawl4AI Session extension copies it in one click. Crawl4AI uses it for this run only and does not store it: no archive, no cache, no log. Your account and the site's terms are your responsibility. The catalog says per input whether it is a secret and whether the session is required or optional; without an optional session the recipe reads what a logged-out visitor sees. A wrong or expired session costs the run's unit: rows is empty, meta.warnings carries bad_session, and meta.blocked names the site.

Staged recipes and health

Every recipe in the catalog carries a stage: published, or staged for a new recipe that runs but is not yet promoted. Each region checks every recipe every six hours and the catalog carries the result as health (ok, fail or unknown, with the last check's rows, time and error). GET /recipes/health returns the same, without a key.

Errors

StatuserrorMeaning
400bad_inputAn unknown input, a required one missing, or a wrong type.
404unknown_recipeNo recipe with that name.
403host_refusedThe recipe tried a host outside its allowed list.
422page_budget · module_limit · bad_moduleThe run needed more pages than the recipe allows, or its module hit a limit.
502 · 504fetch_failed · timeoutA page could not be fetched, or the run passed 300 s.
A failed run still returns usage: the pages fetched before the failure were billed, and it says which. A partial result is never returned as success.

The catalog GET /recipes

curl
curl https://gate.crawl4ai.com/recipes -H "Authorization: Bearer sk_live_..."

Free (no unit). Returns {"recipes": [...]}: for each recipe its name, version, title, stage, health, kind (json, module or script), inputs (type, default, required), output fields, fresh_for_s, tags, a cost hint, run (sync or job) and a description.

MCP/mcp

Use Crawl4AI as native tools inside Claude, Cursor, or any MCP client — no install, just a URL and your key.

terminal
claude mcp add --transport http crawl4ai \
  https://gate.crawl4ai.com/mcp \
  --header "Authorization: Bearer sk_live_..."
Tools exposed: scrape · search · answer · extract · batch · recipes_list · recipe_run (the recipes). Your dashboard's MCP tab pre-fills this with your key.
Get a key → Dashboard llms.txt Open source ↗