scrape (sync)
POST /api/scrape
scrape a single url and get the full result in the response. requires a bearer token — see authentication.
request
curl
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,
"links": true,
"images": true
}
}'response
a 200 response includes the fields you requested via the extract object. fields you didn’t request are omitted from the response.
| field | type | description |
|---|---|---|
url | string | the url actually scraped — after any redirects, in normalized form. /api/map writes its links the same way, so the two agree on the same page. links, images, and internal labels are computed against it |
requested_url | string | the url you requested, echoed verbatim — before any redirects |
content_type | string | mime type of the page (e.g., text/html) |
unsupported_fields | string[] | fields requested but not supported for this content type (e.g. markdown extraction on a pdf) |
markdown | string | cleaned markdown (when extract.markdown: true) |
cleaned_html | string | cleaned html — returned by default (unless extract.cleaned_html: false) |
raw_html | string | raw source html (when extract.raw_html: true) |
links | array | page links (when extract.links: true) — each: { text, href, internal } |
images | array | page images (when extract.images: true) — each: { url, alt } |
screenshot | object | screenshot data (when extract.screenshot is set) — see screenshots |
metadata | object | page metadata — returned by default (unless extract.metadata: false) |
response_meta | object | operation metadata for this response (usage/billing) — see core concepts |
response_meta.usage.credits | integer | credits actually charged for this request (0 on a fully cached result) |
response_meta.usage.engine | string | delivered billing engine: "http", "browser", "screenshot", or "cache" |
response_meta.usage.proxy | string | the proxy tier that actually resolved the request — the resolved tier, never "auto" |
response_meta.usage.screenshot_slices | integer | billed slice-variant increment: 1 when newly produced, otherwise 0 |
warnings | string[] | non-error notices — an output was capped rather than delivered whole (e.g. screenshot_truncated, links_truncated) or couldn’t be extracted (e.g. links_unavailable); filtered to the outputs you requested. see limits and truncation |
response example
{
"url": "https://example.com",
"requested_url": "https://example.com",
"content_type": "text/html",
"markdown": "# Example Domain\n\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\n\n[More information...](https://www.iana.org/domains/example)",
"cleaned_html": "<h1>Example Domain</h1><p>This domain is for use in illustrative examples...</p>",
"links": [
{
"text": "More information...",
"href": "https://www.iana.org/domains/example",
"internal": false
}
],
"metadata": {
"title": "Example Domain",
"og_title": "Example Domain",
"favicon_url": "https://example.com/favicon.ico"
},
"response_meta": {
"usage": {
"credits": 1,
"engine": "http",
"proxy": "basic",
"screenshot_slices": 0
}
}
}metadata fields
the metadata object is included by default. set extract.metadata: false to skip it. every field is optional — only fields present on the page appear in the response; absent fields are omitted entirely (never null), with one exception: metadata.favicon_url may be null when no favicon could be resolved.
| field | type | description |
|---|---|---|
title | string | page <title> |
description | string | meta description |
keywords | string[] | meta keywords |
canonical | string | canonical url from <link rel="canonical"> |
og_url | string | Open Graph url |
og_title | string | Open Graph title |
og_description | string | Open Graph description |
og_type | string | Open Graph type (e.g., website, article) |
og_site_name | string | Open Graph site name |
og_locale | string | Open Graph locale (e.g., en_US) |
og_locale_alternate | string[] | alternate Open Graph locales |
og_image | string | Open Graph image url |
author | string | page author |
date_modified | string | last modified date |
date_published | string | published date |
twitter_site | string | Twitter site handle |
twitter_card | string | Twitter card type |
twitter_description | string | Twitter card description |
twitter_title | string | Twitter card title |
twitter_image | string | Twitter card image url |
robots | string | robots meta directive |
favicon_url | string | best-effort favicon url for the page |
error responses
| status | name | when |
|---|---|---|
| 400 | invalid_url, url_too_long, scrape_error | bad url, url exceeds 8,192 bytes, or scrape failed |
| 400 | unsupported_url_schema | only http and https are supported |
| 400 | url_credentials_not_supported | urls with embedded credentials are rejected |
| 400 | blocked_url | url is not allowed to be scraped |
| 400 | validation_error | request body failed validation |
| 401 | invalid_credentials | invalid or missing api token |
| 403 | antibot_blocked | the target site’s anti-bot protection blocked the scrape |
| 408 | request_timeout | scrape took too long |
| 415 | unsupported_content | target content type not supported for extraction |
| 422 | unsupported_screenshot_output | the request asked only for a screenshot and the content type can’t be screenshotted (json, plain text, markdown, xml) — not billed |
| 422 | too_many_redirects | the target site redirected the request in a loop, or through more hops than we follow. not a bad request; retrying rarely helps |
| 422 | page_too_large | the page’s html was too large to process. terminal — the same url fails the same way, so don’t retry it |
| 429 | too_many_requests, usage_allocation_error | rate limit or credit/concurrency limit hit |
| 499 | client_closed_request | client disconnected before completion |
| 500 | internal_server_error | server-side failure — including a screenshot-only request whose capture failed (not billed) |
| 503 | service_unavailable | transient failure on our end — retry the same request; not an auth problem |
all errors follow the standard { name, message, details? } format. see errors for the full reference, and when no screenshot can be delivered for the screenshot-only failure semantics.
combining extraction options
you can request multiple extraction types in a single call — see extraction options for the full field reference. pass everything you need in the extract object, including a screenshot:
{
"url": "https://news.ycombinator.com",
"extract": {
"markdown": true,
"cleaned_html": true,
"links": true,
"images": true,
"screenshot": { "type": "full_page" }
}
}request only what you need. each field you skip means less processing and a faster response.
if you don’t pass an extract object, you’ll get metadata + cleaned_html back. add markdown,
raw_html, links, images, or screenshot to request more.