/crawl walks a site breadth-first from a start URL and renders every page it finds, exactly the way /scrape would.
Crawls take minutes, so they run as an async job. You submit one, get an id straight away, then read progress and pages from GET /crawl/{id}, or get them pushed to your webhook.
1
Submit
POST /crawl returns 202 with a crawl id.2
Poll
GET /crawl/{id} returns progress plus a page of results. Follow next until it’s null.3
Stop early (optional)
DELETE /crawl/{id} cancels a running crawl. Pages already fetched stay readable.Example request
Example response
202 Accepted, not 200. The crawl has been queued, not completed. To read the pages, see Async jobs.
Scope
A crawl stays inside the boundary you draw. By default that’s the site you pointed it at, two links deep, at most 25 pages.
A crawl stays at or below the path it starts from. One started at
https://docs.stripe.com/payments/ crawls the payments docs, not the whole site its navigation links to. A start URL that names a file, like /docs/intro.html, scopes to its folder. Set crawl_entire_domain: true to follow links anywhere on the site.
Patterns match the path and query, not the whole URL. So ^/docs/ means the docs section, and a pattern that contains the hostname never matches. Exclusions win over inclusions. An invalid regular expression is rejected with a 400 when you submit, not ignored halfway through.
Duplicates
http:// and https://, www. and the bare domain, a trailing slash and /index.html all name the same page, so each page is fetched and billed once. A page that redirects onto one the crawl already has is dropped as a duplicate.
Sitemap seeding and depth
Withuse_sitemap on (the default), the crawl starts from the site’s sitemap as well as following links.
Pages from the sitemap sit at depth 0: the site handed them over, rather than a link leading to them. That makes one combination especially useful:
Politeness
Crawls obeyrobots.txt by default: both its Disallow rules and its Crawl-delay. Set respect_robots_txt: false to ignore the rules.
robots.txt is always read, even with the rules turned off, because its Sitemap: entries seed the crawl. Use use_sitemap: false to skip seeding.delay_ms adds a pause between pages, and concurrency (1 to 5, default 2) sets how many pages are fetched in parallel. A Crawl-delay in robots.txt wins whenever it asks for a longer pause than delay_ms.
Rendering each page
scrape_options takes the same options as /scrape, so a crawled page and a scraped page are the same object.
When pages are missing
GET /crawl/{id}/errors explains a crawl that came back smaller than expected, for free:
failed: pages that were fetched and failed, each with anerror_codethat says whether trying again could help.skipped: URLs the crawl found but didn’t fetch, counted by reason with a few examples each:limit,depth,out-of-scope,excluded,not-included,robots,off-site,file,invalid,blocklistandduplicate.
limit count means the crawl ran out of budget. A large out-of-scope count usually means it needed crawl_entire_domain.
Webhooks
Passwebhook to have progress pushed to you instead of polling:
The
POST /crawl response includes webhook.secret, and that’s the only place it appears, so store it. See Webhooks for the payload, signatures and retries.
Restarts
A crawl survives restarts on our side. If the worker running it stops mid-crawl, for a deploy or a crash, another worker resumes it from a checkpoint saved every few seconds. At most the last few seconds of pages are fetched again, and a page is never returned or billed twice.Credits
Submitting is free. You’re charged 1 credit per page the crawl returns, or 4 whenscrape_options asks for summary, json or check_prompt_injection. You pay for pages fetched, never for limit, and failed pages are free. See Credits.
Next
Turn docs into markdown
A recipe for feeding a whole docs site to an LLM.
API reference
Every parameter, and a playground to try it.