Crawl a whole site
https://api.crawl4ai.com/siteEvery page under a URL, as Markdown, level by level: the start page (level 0), the pages it links to (level 1), and so on to depth. It follows only links inside the site, through your include and exclude filters, and stops at max_pages. A site crawl runs only as a job: submit it, follow it, then read the rows. A job runs for at most one hour. To list a site's URLs without fetching them, use /scan.
# 1. submit -> job id JOB=$(curl -s https://api.crawl4ai.com/site \ -H "Authorization: Bearer sk_live_..." -H "Content-Type: application/json" \ -d '{ "url": "https://docs.crawl4ai.com/", "depth": 2, "max_pages": 50, "include": "/core" }' | jq -r .job_id) # 2. poll until it ends (done, or paused with a reason) while true; do S=$(curl -s https://api.crawl4ai.com/site/jobs/$JOB -H "Authorization: Bearer sk_live_..." | jq -r .status) case "$S" in done|paused) break;; esac; sleep 5 done # 3. read the rows (NDJSON, one line per page) curl -s https://api.crawl4ai.com/site/jobs/$JOB/results \ -H "Authorization: Bearer sk_live_..."
Body
urlrequiredstring | The start page, a public http/https URL. |
depthint | How many levels of links to follow below the start page. Default 2, max 5. |
max_pagesint | The most pages fetched. Default 100, max 500; a larger value is cut to 500. |
includestring | Follow only matching URLs: a path prefix ( |
excludestring | Never follow matching URLs, in the same form as |
include_subdomainsbool | Also follow links to subdomains of the start URL's host. Default false. |
contentstring | Default md
|
The job
{ "job_id": "j_...", "urls": 1, "status": "pending", "completes_by_ms": 1791334800000, "usage": { "units": 1, "boxes": ["jobs"] } }
| Route | Does |
|---|---|
| POST /site | Submit the body above. Returns 202 with the job_id. |
| GET /site/jobs/{id} | The status (pending, running, done, paused with a paused_reason), the counts, completes_by_ms and the job's usage. |
| GET /site/jobs/{id}/results | The rows, one JSON object per line, each with a seq number. Page with ?after=N (500 per page). |
| POST /site/jobs/{id}/cancel | Stop a running job. It ends as paused. |
The same job also reads at /scrape/jobs/{id} and /scrape/jobs/{id}/results.
Each row
{ "url": "https://docs.crawl4ai.com/core/installation", "level": 1, "title": "Installation", "status": 200, "links": 61, "markdown": "# Installation\n\n...", "seq": 3 }
| Field | Description |
|---|---|
| url | The page. |
| level | 0 for the start page, 1 for the pages it links to, and so on. |
| title | The page's <title>, else its first # heading. |
| status | The site's HTTP status for the page. |
| links | How many internal links the page had. |
| markdown · html | The page's content, by content: markdown for md, html for html, neither for none. |
| error | Only on a page that failed: why. |
Credits and errors
A site crawl costs 1 job unit when the job is accepted (the usage of the 202), plus each page it fetches, priced as a /scrape of that page. Each page is its own receipt in your usage history. No flat recipe price applies to a site crawl.
| Status | error | Meaning |
|---|---|---|
| 400 | bad_input | The body is not a JSON object, or an input has the wrong type. |
| 402 | no_credit | Your balance is empty. Start a plan, or buy extra credits on your plan, and submit again. |
| 404 | not_available | The service is not open to your account yet. |
| 405 | POST only | A method other than POST on /site. |
A url that is not an http/https page gives a job with no rows.
Run this call in the playground and see the real answer, the time and the units.
Next
Every URL of one site, without crawling it.
Scrape a pagePOST /scrapeFetch a page and return clean Markdown (and/or HTML).
Something unclear or wrong on this page? Tell us.