Recipes
https://api.crawl4ai.com/recipes/{name}Ready-made scrapers. A recipe knows one site: you send its inputs (a URL, an id, a search word), you get rows back, every row with the same named columns. Find a recipe in the catalog, run it with one call. Browse and try them at /recipes/.
Browse every recipe: /recipes/
Find a recipe GET /recipes
curl https://api.crawl4ai.com/recipes -H "Authorization: Bearer sk_live_..."
Free (no credit). Returns {"recipes": [...]}, one object per recipe. A recipe's output lists its columns in order, and output_about gives one sentence per column: what it holds and in which form (an id as a string, a time in ISO 8601, an object with its keys).
{ "name": "tiktok-post", "version": 1, "title": "One TikTok post", "kind": "module", "stage": "published", "run": "both", "tags": ["social"], "description": "One public TikTok video post as one row: its text and hashtags, ...", "inputs": { "post": { "type": "string", "default": null, "required": true, "example": "7654036719242726670", "secret": false } }, "output": ["post_id", "url", "description", "hashtags", "created_at", "author", "stats", ...], "output_about": { "post_id": "TikTok's id of the post (digits, as a string).", "created_at": "When it was posted, ISO 8601 UTC (2026-06-22T02:11:46Z).", "stats": "{plays, likes, comments, shares, saves}, integers, as TikTok rounds them on the page.", ... }, "cost": "1 unit; 1 record when it falls back", "fresh_for_s": 900, "health": { "status": "ok", "rows": 1, "ms": 1240, "checked_at": 1791331200 } }
| Field | Description |
|---|---|
| name | The recipe's name, the {name} in the run URL. |
| title · description · tags | What it reads, in a line and in two or three sentences; tags holds its one category (news, social, video, shopping, jobs, dev tools, knowledge, finance, reviews). |
| inputs | One entry per input: type (string, int, bool, date), default, required, an example value, secret. A session input also has need: required or optional. |
| output | The column names of every row, in order. |
| output_about | One sentence per column, keyed by the column name. |
| cost | A hint in words: the units a run usually takes, and "record" when the recipe can take rows from a second source (see the cost below). |
| run | sync (one call, the rows in the answer), job (submit, then read the rows) or both. |
| fresh_for_s | How long a result stays in the cache (see below). |
| stage · health · version · kind | See "Staged recipes and health". kind is json, module or script. |
Run one
curl https://api.crawl4ai.com/recipes/hn-hiring \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "thread": 49522897, "keyword": "Rust" }'
<input>string · int · bool · date | One 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 |
bypass_cachebool | Run again even when a fresh result is cached (see below). |
{ "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": [] } }
| Field | Description |
|---|---|
| rows | One object per row, exactly the recipe's output columns, in order. A column the page did not have is null. |
| usage | What this run used, 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), each LLM call (extract, its step) and, for some recipes, one records line with the count of rows taken from the second source. Each line is one receipt in your usage history. |
| meta | pages_fetched, llm_calls, elapsed_ms, from_cache, and warnings (columns that came back empty, unmatched joins, a module's log lines). |
Job recipes
A recipe with run: job reads many pages (a long list, every comment of a video) and can take minutes. Submit it, follow it, then read the rows. A recipe with run: both takes either call. A job recipe sent to the sync URL answers 400 use_jobs. A job runs for at most one hour.
curl https://api.crawl4ai.com/recipes/youtube-comments/jobs \ -H "Authorization: Bearer sk_live_..." -H "Content-Type: application/json" \ -d '{ "video": "dQw4w9WgXcQ", "limit": 500 }' # → 202 {"job_id": "j_...", "status": "pending"}
| Route | Does |
|---|---|
| POST /recipes/{name}/jobs | Submit the recipe's inputs, the same body as a sync run. Returns a job_id. |
| GET /recipes/jobs/{id} | The status (pending, running, done, paused with a paused_reason), the counts and the job's usage. |
| GET /recipes/jobs/{id}/results | The rows, one JSON object per line, each with a seq number. |
| POST /recipes/jobs/{id}/cancel | Stop a running job. It ends as paused. |
The cost
A run costs the call itself (the recipes line in the pricing table), plus each page it fetched and each LLM step, priced as a /scrape or an /extract you send yourself. Each of them is a line in usage.lines and its own receipt in your usage history; usage.units adds them up. The catalog's cost gives the usual figure before you call.
Some recipes have a second source: when the site's own page fails, they take the rows from it. Each row taken that way is one record, and a record costs 3 credits: $3 per 1,000 records, at $0.001 a credit. The catalog's cost says "record" for these recipes, and the receipt's records line gives the count. Records are charged only on a successful run, never on a cache hit. Each account has a daily limit of records; past it the run answers 503 vendor_off.
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 1 (the call only: no page, no record), 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; ours for a recipe that reads our own crawler and needs no check). GET /recipes/health returns the same, without a key.
Errors
| Status | error | Meaning |
|---|---|---|
| 400 | bad_input | An unknown input, a required one missing, or a wrong type. |
| 400 | use_jobs | A job recipe sent to the sync URL: submit it to /recipes/{name}/jobs. |
| 404 | unknown_recipe | No recipe with that name. |
| 403 | host_refused | The recipe tried a host outside its allowed list. |
| 422 | page_budget · module_limit · bad_module | The run needed more pages than the recipe allows, or its module hit a limit. |
| 499 | cancelled | The job was stopped before it finished. |
| 502 · 504 | fetch_failed · timeout | A page could not be fetched, or the run passed 300 s (a job: one hour). |
| 503 | vendor_off | The site's page failed and the recipe's second source reached today's limit, or is paused. Try again later; the limit resets each day (UTC). |
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.
Run this call in the playground and see the real answer, the time and the units.
Next
Watch a page and get told when it changes in the way you care about.
Prices from pricing pagesPOST /priceGive it websites, get their prices.
Something unclear or wrong on this page? Tell us.