Skip to Content

extraction options

we return the page metadata and cleaned_html by default. use the extract object to add more formats — markdown, raw_html, links, images, screenshot — or to turn off a default. if you don’t pass an extract object, you get metadata + cleaned_html back.

every option below is a boolean flag on the extract object (except screenshot, which is covered on its own page).

markdown

set extract.markdown: true to get a cleaned markdown representation of the page content. scripts, navigation, footers, and other non-content elements are stripped, then the remaining html is converted to markdown.

best for: ai/llm pipelines, content indexing, rag systems.

request

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", "requested_url": "https://example.com", "content_type": "text/html", "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples in documents...", "metadata": { "title": "Example Domain" } }

the cleaned markdown is returned in the markdown field. full responses also carry a response_meta object reporting credits, engine, and proxy tier — see the sync response reference.

cleaned html

cleaned html (the cleaned_html field) is returned by default. scripts, styles, and non-content elements are removed, and your cleanup block is applied before the result is built. to skip it, set extract.cleaned_html: false.

request

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

response (condensed)

{ "url": "https://example.com", "requested_url": "https://example.com", "content_type": "text/html", "cleaned_html": "<h1>Example Domain</h1><p>This domain is for use in illustrative examples...</p>", "metadata": { "title": "Example Domain" } }

the cleaned html is returned in the cleaned_html field.

raw html

set extract.raw_html: true to get the page’s source html without the readability cleaning and markdown conversion applied to cleaned_html and markdown. nothing is stripped for readability — you get the full document as returned by the server or browser.

one thing to know: cleanup never applies to raw_html. neither ads_and_popups nor your own exclude_selectors touch it — raw_html is always the page as it arrived, before anything was removed. that is deliberate: it means you can always get back what we started from, whatever cleanup you asked for. for non-html responses, raw_html is the untouched fetched body.

use this when you need to parse the page yourself or need access to elements that the cleaning step would remove.

request

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

response (condensed)

{ "url": "https://example.com", "requested_url": "https://example.com", "content_type": "text/html", "raw_html": "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>...</html>", "metadata": { "title": "Example Domain" } }

the raw source is returned in the raw_html field.

set extract.links: true to get every link on the page. each link includes:

fieldtypedescription
textstringlink anchor text
hrefstringthe link url as written on the page, resolved to an absolute url — verbatim otherwise (query string and fragment preserved)
internalbooleantrue if the link points to the same domain — www and the bare domain are equivalent; other subdomains count as external

beyond absolutization, hrefs aren’t cleaned: duplicates and self-links survive, and deduplication is up to you. empty, unparseable, and non-http(s) hrefs (mailto:, tel:, javascript:) are dropped.

response (condensed)

{ "url": "https://example.com", "requested_url": "https://example.com", "content_type": "text/html", "links": [ { "text": "More information...", "href": "https://www.iana.org/domains/example", "internal": false }, { "text": "About Us", "href": "https://example.com/about", "internal": true } ], "metadata": { "title": "Example Domain" } }

images

set extract.images: true to get every image on the page. each image includes:

fieldtypedescription
urlstringabsolute image url (see note below)
altstring | nullalt text, or null if not set

url is always absolute: document-relative src values are resolved against the full page url (browser-parity, matching link extraction), and any query string on the original src is preserved.

response (condensed)

{ "url": "https://example.com", "requested_url": "https://example.com", "content_type": "text/html", "images": [ { "url": "https://example.com/logo.png", "alt": "Example logo" }, { "url": "https://example.com/hero.jpg", "alt": null } ], "metadata": { "title": "Example Domain" } }

metadata

metadata is returned by default on every scrape request. you don’t need to opt into it — it’s always there unless you explicitly disable it with extract.metadata: false.

fieldtype
titlestring
descriptionstring
keywordsstring[]
canonicalstring
og_urlstring
og_titlestring
og_descriptionstring
og_typestring
og_site_namestring
og_localestring
og_locale_alternatestring[]
og_imagestring
authorstring
date_modifiedstring
date_publishedstring
twitter_sitestring
twitter_cardstring
twitter_descriptionstring
twitter_titlestring
twitter_imagestring
robotsstring
favicon_urlstring

all fields are optional — availability depends on what the target page provides. fields that aren’t present on the page are omitted from the response entirely, with one exception: favicon_url is a best-effort lookup (checked against <head> icon hints and the manifest, falling back to /favicon.ico) and can be returned as null when no favicon is found, instead of being omitted.

limits and truncation

extraction is bounded per page, so a single pathological page can’t produce an unbounded response. the caps are generous — real pages sit far below them — and when one is reached we truncate that output and tell you, in the response’s top-level warnings array. nothing is capped silently.

whatcapwarning code
links per page30,000links_truncated
inline images per page10,000inline_images_truncated
body html, feeding raw_html10,000,000 charactersraw_html_truncated
head html, feeding metadata2,000,000 charactersmetadata_truncated
full-page screenshot capture height15,000pxscreenshot_truncated
{ "warnings": ["links_truncated", "raw_html_truncated"] }

warnings is a flat array of stable string codes — switch on the code, don’t match on message text. truncated html is cut back to a tag boundary, so what you get still parses.

when a field can’t be extracted

truncation means the field is there but capped. the other half of warnings covers the opposite case: an optional field whose extraction failed outright. rather than failing your whole scrape over one section, we return everything else and name what’s missing.

what failedyou getwarning code
link extractionlinks omitted or emptylinks_unavailable
image extractionimages omitted or emptyinline_images_unavailable
metadata extractionmetadata omitted or emptymetadata_unavailable

this is what lets you tell “the page genuinely had no links” apart from “we couldn’t read them” — an empty array alone can’t distinguish the two.

the page body is the exception. if the body can’t be extracted there’s nothing worth returning, so the scrape fails with an error instead of handing you a hollow 200 — and a failed scrape isn’t billed.

which warnings come back

warnings is stored alongside the result, so it survives past the original call: cache hits and async result fetches carry the same codes a fresh scrape would.

they’re filtered to the outputs you actually asked for — request markdown only and you won’t hear about the page’s links. raw_html_truncated is the exception and always surfaces, because a truncated body feeds markdown and cleaned_html too.

excluding content

the cleanup block says what comes off the page before anything is built from it. it has two fields.

ads_and_popups is on by default and removes ads, cookie banners, consent dialogs and chat widgets. set it false when you want the page as-is — or when a site refuses to serve content to a client that looks like an ad blocker.

exclude_selectors takes an array of css selectors for anything the default doesn’t catch: your own navigation, footers, sidebars, or site-specific noise. at most 100 selectors, each at most 500 characters.

both shape markdown, cleaned_html, links, images and the screenshot. neither touches raw_html.

{ "url": "https://example.com", "extract": { "markdown": true }, "cleanup": { "ads_and_popups": true, "exclude_selectors": ["nav", "footer", ".cookie-banner", "#sidebar"] } }

using cleanup.exclude_selectors disables caching for that request — each call with selectors runs a fresh scrape. cleanup.ads_and_popups does not: it is part of the cache key instead, so both settings stay cacheable.

disabling the defaults

metadata and cleaned_html are on by default. set either to false inside extract to skip it and reduce response size.

{ "url": "https://example.com", "extract": { "markdown": true, "metadata": false, "cleaned_html": false } }

this is useful when you only care about one format — here, markdown — and want the leanest possible response.

request only what you need. a request for just markdown is faster and lighter than one requesting everything. the unsupported_fields array in the response tells you if any requested options couldn’t be fulfilled.