webfetch
Documentation

One endpoint

POST /v1/scrape — a URL in, formatted content out. Everything else is account management.

The request

curl
curl https://api.webfetch.run/v1/scrape \
  -H "Authorization: Bearer $SCRAPE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "formats": ["markdown"],
    "egress": "residential",
    "persist": true
  }'
FieldTypeWhat it does
urlstringRequired. The page to fetch. Must be public http(s).
formatsstring[]Defaults to ["markdown"]. Names are case-insensitive; an unknown name is a 400 that lists the valid ones.
egress"direct" | "datacenter" | "residential"Where the request leaves from. Defaults to direct, the cheapest tier.
onlyMainContentbooleanDefaults to true: narrows html and markdown to the main content. Set false for the whole DOM.
waitForSelectorstringWait for this CSS selector before capturing.
persistbooleanStore the capture and return a link that renders it from our origin. Off by default.
rotationsnumberHow many exits to try on a wall. Default 3, clamped 0-5.
parallelProbesnumberHow many exits to try concurrently. Default 3, clamped 1-5.
solvebooleanWhether a CAPTCHA may be attempted. Default true.
solveTimeoutMsnumberCap on one solve. Default 20000, clamped 5000-120000.
timeoutnumberOverall budget for the request. Default 120000, clamped 5000-300000.
sessionobjectReplay a cleared session: the session.cookies (and egressPort) from a previous response.
attributes{selector,attribute}[]For the attributes format. Up to 20.
summaryPromptstringInstruction for the summary format.
jsonOptions{schema}JSON Schema for the json format.

Formats

Ask for any combination. You get back only what you asked for.

FormatReturnsWhat it is
markdownstringThe page as markdown. The default when no format is given.
htmlstringCleaned HTML. Narrowed by onlyMainContent, which defaults to true.
rawHtmlstringThe document exactly as received, never narrowed.
rawBase64stringThe document bytes as base64. UTF-8 safe.
textstringVisible text only, no markup.
screenshotstringBase64 PNG of the rendered page.
linksstring[]Absolute URLs found on the page.
images{src,alt,...}[]Absolute image URLs with alt text and dimensions when present.
metadataobjectThe page's head tags: title, description, og:*, canonical, language.
brandingobjectColours, fonts and logo, extracted deterministically.
attributesobjectNamed attributes per CSS selector. Send `attributes: [{selector, attribute}]`.
summarystringA short abstract, generated by a model.
jsonobjectStructured extraction. Send `jsonOptions: { schema }`.

The response

200 OK
{
  "success": true,
  "data": {
    "markdown": "# Example Domain\n\nThis domain is for use...",
    "metadata": {
      "url": "https://example.com",
      "finalUrl": "https://example.com/",
      "title": "Example Domain",
      "status": 200,
      "verdict": "ok",
      "tier": "browser/residential",
      "attempts": 1,
      "bytes": 1258,
      "session": { "cookies": ["..."], "egressPort": 10006 }
    }
  },
  "credits": { "charged": 10, "remaining": 990 }
}

Keep metadata.session and send it back as session on the next request. A cleared session is bound to the exit that earned it, and reusing it turned repeat queries from 38 s into 6-8 s.

Errors

A failure is always success: false with a machine-readable code, and always costs 0 credits.

CodeHTTPMeaning
invalid_request400The body did not validate — a missing url, an unknown format, a bad selector shape.
target_not_allowed400The URL is private, internal, or not a public http(s) address.
egress_unavailable400You asked for an egress tier this deployment has not configured. Refunded in full.
insufficient_credits402The balance cannot cover the maximum this request could cost. Nothing ran.
blocked200The site returned a wall instead of content. `success: false`, 0 credits charged.
timeout504The request exceeded its `timeout` budget. 0 credits charged.
fleet_unavailable503Our own renderer could not be reached. Our fault, refunded in full.

Keys

Create a key in the console. The plaintext is shown once; we store only a SHA-256 hash. Send it as Authorization: Bearer sk_live_....

API documentation — webfetch