Skip to Content

scrape

turn any url into structured data. extract markdown, html, links, images, and screenshots from any web page with a single POST request.

crawlbrulee handles the hard parts — JavaScript rendering, managed proxy routing, caching — so you can focus on what to do with the data, not how to get it.

endpoints

the scrape api has four endpoints: one for synchronous scraping, and three for the async workflow.

methodpathdescription
POST/api/scrapescrape a url synchronously
POST/api/scrape/asyncsubmit an async scrape job
GET/api/scrape/status/{job_id}check async job status
GET/api/scrape/result/{job_id}get async job result

see scrape (sync) and scrape (async) for endpoint-specific details.

request body

both POST /api/scrape and POST /api/scrape/async accept the same json body, plus an optional webhook object accepted only by the async endpoint (see webhooks). only url is required — everything else is optional.

fieldtyperequireddefaultdescription
urlstringyesthe url to scrape
extractobjectno{ "metadata": true, "cleaned_html": true }what to extract. see extraction options
extract.metadatabooleannotruereturn page metadata — on by default; set extract.metadata: false to skip
extract.cleaned_htmlbooleannotruereturn cleaned html (main content) — on by default
extract.markdownbooleannofalsereturn cleaned markdown
extract.raw_htmlbooleannofalsereturn original unprocessed html
extract.linksbooleannofalsereturn all page links with href, text, and internal/external classification
extract.imagesbooleannofalsereturn all inline images found on the page, with url and alt text
extract.screenshotobjectnocapture a screenshot — see screenshots
cacheobjectnocache control options — see caching
cache.max_ageinteger | datetimeno172800 (2 days)maximum age of cached result in seconds, or an iso 8601 timestamp
require_jsbooleannofalserender the page with JavaScript before extraction
cleanupobjectno{ ads_and_popups: true }what is removed before any output is built — never applies to raw_html
cleanup.ads_and_popupsbooleannotrueremove ads, cookie banners, consent dialogs and chat widgets
cleanup.exclude_selectorsstring[]noyour own css selectors to remove (max 100, each max 500 chars)
proxystringno"auto"proxy tier: "basic", "advanced", or "auto" — see proxies & location
locationobjectnolocale + country emulation for the request
location.localestringnobcp-47 locale (e.g. en-US, de-DE) — sets Accept-Language and navigator.language
location.countrystringnocase-insensitive two-letter code (e.g. US, DE) or pseudo-value eu/europe — sets the proxy exit country
webhookobjectnoasync only — completion webhook config; see webhooks

a few things worth knowing

  • extract has defaults. omit it and you get metadata + cleaned_html. metadata and cleaned_html are on by default — set either to false to drop it; markdown, raw_html, links, images, and screenshot are opt-in. request only what you need for faster responses and smaller payloads.
  • cache.max_age set to 0 forces a fresh scrape, bypassing any cached result.
  • require_js adds latency because the page is fully rendered before extraction. only use it for JavaScript-rendered pages — see javascript rendering below.
  • cleanup runs before extraction, so removed elements won’t appear in markdown, cleaned_html, link/image output, or the screenshot. it never applies to raw_html — that is always the page as it arrived. cleanup.exclude_selectors disables caching for the request; cleanup.ads_and_popups does not.
  • proxy: "auto" is the default when you omit proxy. it tries basic first and upgrades to advanced when needed. the request reserves its engine’s advanced-proxy ceiling — 15 credits without a screenshot, 25 with one — then bills at the engine and proxy that delivered. see credits & pricing.
  • location emulates a locale + country. location.locale sets Accept-Language/navigator.language; location.country sets the proxy exit country, and also accepts the pseudo-values eu (a random eu member state) and europe (a broader European set including the uk and non-eu countries). useful for geo-targeted or localized pages.

javascript rendering

by default, we extract content the fastest way we can. set require_js: true to render the page with JavaScript before extraction:

{ "url": "https://example.com", "require_js": true, "extract": { "markdown": true } }

use it when:

  • the page is a single-page application (React, Vue, Angular)
  • content is loaded dynamically via JavaScript
  • a default scrape returns empty or incomplete results

browser delivery has a 3-credit engine base before the proxy multiplier. leave require_js off unless the page needs it. you do not need it for screenshots: screenshot requests always render and use the 5-credit screenshot base when the image is delivered.

choosing sync vs async

both modes accept the same request body — async additionally accepts an optional webhook object (see webhooks) — and return the same response schema. the difference is how you receive results.

sync (POST /api/scrape) returns the result directly in the http response. use it when you need data immediately and the page loads in under ~30 seconds.

async (POST /api/scrape/async) returns a job_id immediately. you then receive the result in one of two ways: poll for status and fetch it when the job completes, or register a webhook and have us notify your endpoint the moment it’s done — no polling required. use async for batch pipelines, long-running pages, or queue-driven architectures.

syncasync
response timewaits for full resultreturns job_id immediately
best forreal-time lookups, single pagesbatch processing, background jobs
timeout handlingclient-side timeout applies; the server also enforces a request timeout (request_timeout, http 408)server keeps working regardless
concurrencyshares org poolshares org pool

not sure which to use? start with sync. switch to async when you need to process many urls concurrently or encounter timeout issues with slow-loading pages.

quick example

scrape a page and get markdown:

curl -X POST https://api.crawlbrulee.com/api/scrape \ -H "Authorization: Bearer $CRAWLBRULEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "extract": { "markdown": true } }'

response (condensed):

{ "url": "https://example.com", "content_type": "text/html", "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples ...", // cleaned_html is enabled by default, so it comes back even though we only asked for markdown "cleaned_html": "<h1>Example Domain</h1><p>This domain is for use in illustrative examples...</p>", // metadata is enabled by default too "metadata": { "title": "Example Domain" // ...more metadata fields when present on the page }, "response_meta": { "usage": { "credits": 1, "engine": "http", "proxy": "basic", "screenshot_slices": 0 } } }

the response is a flat json object — the extracted content plus page metadata and a response_meta object reporting the charged engine, proxy, slices, and credits (see core concepts). fully cached responses have engine: "cache" and a 0-credit base; a newly produced screenshot-slice variant adds one flat credit (see caching).

what’s next