API reference
Website API
Fetch any public URL and get its HTML back as JSON. Turn on a real browser, premium or stealth proxies, browser actions, screenshots or AI extraction with one parameter each.
Send parameters in the query string, a JSON body or a form body; if both are used, body values win. Booleans accept true or 1. URL-encode url when it goes in a query string.
Parameters
- urlstringRequiredThe page to scrape: a full
http://orhttps://URL, up to 4,096 characters. Private and local network addresses (localhost, 10.x, 192.168.x and similar) are rejected. - render_jsbooleanDefault
falseLoad the page in a real browser and run its JavaScript. 5 credits. Details - premium_proxybooleanDefault
falseRoute the request through residential IPs. 10 credits. Details - stealth_proxybooleanDefault
falseOur strongest option for heavily protected sites; always renders. 75 credits. Details - screenshot"true" | "full_page"Return a PNG of the viewport or the whole page as base64. Turns on rendering. Details
- js_scenarioJSONBrowser actions to run before the HTML is captured: click, fill, scroll, wait, evaluate. Up to 49 steps. Turns on rendering. Details
- waitinteger, msExtra time to wait after the page loads, 0 to 45000. Rendering only. Details
- wait_forCSS selectorWait until this element appears. Rendering only.
- wait_browserstringDefault
domcontentloadeddomcontentloaded,load,networkidle0ornetworkidle2. Rendering only. - block_adsbooleanDefault
falseBlock ads while rendering. - block_resourcesbooleanDefault
falseSkip images and other heavy resources while rendering, for faster pages. - cookiesstring | object | arrayCookies to send with the request. Not available with
stealth_proxy. Details - timeoutinteger, msDefault
45000Give up after this long. The maximum is 45000; larger values are capped. - ai_querystringDescribe the data you want in plain English and get it back as JSON in
ai_result. +5 credits. Details - ai_schemaJSON schemaLike
ai_query, but with the exact fields and types you want. +5 credits. - window_widthintegerAccepted for compatibility with other scraping APIs. Must be between 320 and 7680 if sent.
- window_heightintegerAccepted for compatibility. Must be between 240 and 4320 if sent.
- country_codestringAccepted for compatibility. Requests aren't guaranteed to come from a particular country.
google.com/search?q=…) are rejected with a pointer to the Google Search API, which returns them as structured JSON. If you send one with premium_proxy or stealth_proxy, it is answered by the Google Search API directly and costs 10 credits.Request
curl -G "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=https://example.com" \ --data-urlencode "render_js=true"
import requests response = requests.get( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, params={"url": "https://example.com", "render_js": "true"}, timeout=60, ) data = response.json() print(data["status"], data["credits_used"])
const params = new URLSearchParams({ url: "https://example.com", render_js: "true" }); const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); console.log(data.status, data.credits_used);
<?php $query = http_build_query(["url" => "https://example.com", "render_js" => "true"]); $ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); echo $data["status"], " ", $data["credits_used"], PHP_EOL;
Response
{ "success": true, "url": "https://example.com", "html": "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\">…", "status": 200, "duration": "1.42", "credits_used": 5, "job_id": "2418eddc-2154-4619-825d-697d8625dbf3" }
JavaScript rendering
By default the API makes a plain HTTP request and returns the HTML exactly as the server sent it. That's the fastest and cheapest option (1 credit) and works for most static pages.
Many sites build their content in the browser. With render_js=true the page loads in a real browser, its scripts run, and you get the rendered HTML for 5 credits. Rendering also switches on automatically when you ask for a screenshot, a js_scenario or a stealth proxy.
- render_jsbooleanDefault
falseRender the page in a browser. - block_adsbooleanDefault
falseBlock ads while rendering. - block_resourcesbooleanDefault
falseSkip images and other heavy resources while rendering. Pages load faster; leave it off if you need what those resources produce.
html is missing content you can see in your browser, add render_js=true. The wait options help when content appears late.Request
curl -G "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=https://quotes.toscrape.com/js/" \ --data-urlencode "render_js=true" \ --data-urlencode "block_resources=true"
import requests response = requests.get( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, params={ "url": "https://quotes.toscrape.com/js/", "render_js": "true", "block_resources": "true", }, timeout=60, ) data = response.json() print(data["html"])
const params = new URLSearchParams({ url: "https://quotes.toscrape.com/js/", render_js: "true", block_resources: "true", }); const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); console.log(data.html);
<?php $query = http_build_query([ "url" => "https://quotes.toscrape.com/js/", "render_js" => "true", "block_resources" => "true", ]); $ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); print_r($data["html"]);
Premium & stealth proxies
Some sites block traffic from data centers. Two parameters route your request differently:
- premium_proxybooleanDefault
falseResidential IPs for sites that block data-center traffic. 10 credits, with or withoutrender_js. - stealth_proxybooleanDefault
falseOur most robust option, for heavily protected sites. Always renders JavaScript. 75 credits. Takes precedence ifpremium_proxyis also set, and can't be combined withcookies.
Start with the cheapest mode that works and step up only when you're blocked. Blocked attempts (403, 429 and similar answers from the site) are refunded, so trying a cheaper mode first is low-risk.
| Mode | Parameters | Credits |
|---|---|---|
| HTTP | none | 1 |
| Rendered | render_js | 5 |
| Premium | premium_proxy | 10 |
| Stealth | stealth_proxy | 75 |
Request
curl -G "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=https://shop.example.com/p/123" \ --data-urlencode "premium_proxy=true" \ --data-urlencode "render_js=true"
import requests response = requests.get( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, params={ "url": "https://shop.example.com/p/123", "premium_proxy": "true", "render_js": "true", }, timeout=60, ) data = response.json() print(data["status"], data["credits_used"])
const params = new URLSearchParams({ url: "https://shop.example.com/p/123", premium_proxy: "true", render_js: "true", }); const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); console.log(data.status, data.credits_used);
<?php $query = http_build_query([ "url" => "https://shop.example.com/p/123", "premium_proxy" => "true", "render_js" => "true", ]); $ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); echo $data["status"], " ", $data["credits_used"], PHP_EOL;
Waiting for content
When a page fills in after it loads, tell the browser what to wait for. These options apply to rendered requests, and all waiting counts toward the 45-second time limit.
- wait_browserstringDefault
domcontentloadeddomcontentloaded: the HTML has been parsed.load: the page and its resources have loaded.networkidle0: no network connections for 500 ms.networkidle2: no more than two connections for 500 ms.
domcontentloaded. - wait_forCSS selectorWait until an element matching this selector appears, for example
.product-price. - waitinteger, msA fixed extra pause, 0 to 45000. Values outside that range are rejected with
400.
wait_for over a long wait: it finishes as soon as the content is there.Request
curl -G "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=https://quotes.toscrape.com/js/" \ --data-urlencode "render_js=true" \ --data-urlencode "wait_for=.quote" \ --data-urlencode "wait_browser=networkidle2"
import requests response = requests.get( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, params={ "url": "https://quotes.toscrape.com/js/", "render_js": "true", "wait_for": ".quote", "wait_browser": "networkidle2", }, timeout=60, ) data = response.json() print(data["html"])
const params = new URLSearchParams({ url: "https://quotes.toscrape.com/js/", render_js: "true", wait_for: ".quote", wait_browser: "networkidle2", }); const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); console.log(data.html);
<?php $query = http_build_query([ "url" => "https://quotes.toscrape.com/js/", "render_js" => "true", "wait_for" => ".quote", "wait_browser" => "networkidle2", ]); $ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); print_r($data["html"]);
Browser actions (js_scenario)
Click, type, scroll and run JavaScript before the HTML is captured. Send a JSON array of instructions, or an object with an instructions array. Steps run in order, up to 49 per request, and a scenario always renders the page (5 credits).
| Instruction | What it does |
|---|---|
| {"click": "#accept"} | Click the element matching the selector. |
| {"fill": ["#q", "shoes"]} | Type text into an input. |
| {"wait": 1500} | Pause for a number of milliseconds. |
| {"wait_for": ".results"} | Wait until the selector appears. |
| {"wait_for_and_click": "#more"} | Wait for the selector, then click it. |
| {"scroll_y": 1200} | Scroll vertically by pixels. scroll_x scrolls horizontally. |
| {"infinite_scroll": {…}} | Keep scrolling to load more: max_count scrolls, delay ms between them (default 1000), and an optional end_click.selector. |
| {"evaluate": "document.title"} | Run JavaScript in the page. |
A report of the run comes back in the scenario field. Unknown instruction names are ignored.
fault: "user" and charged, so validate it first.Request
curl -X POST "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://shop.example.com", "js_scenario": [ {"click": "#accept-cookies"}, {"fill": ["input[name=q]", "running shoes"]}, {"click": "button[type=submit]"}, {"wait_for": ".results"}, {"scroll_y": 1500}, {"evaluate": "document.title"} ] }'
import requests response = requests.post( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, json={ "url": "https://shop.example.com", "js_scenario": [ {"click": "#accept-cookies"}, {"fill": ["input[name=q]", "running shoes"]}, {"click": "button[type=submit]"}, {"wait_for": ".results"}, {"scroll_y": 1500}, {"evaluate": "document.title"}, ], }, timeout=60, ) data = response.json() print(data["scenario"])
const response = await fetch("https://scrapingbot.io/api/v1/scrape", { method: "POST", headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://shop.example.com", js_scenario: [ { click: "#accept-cookies" }, { fill: ["input[name=q]", "running shoes"] }, { click: "button[type=submit]" }, { wait_for: ".results" }, { scroll_y: 1500 }, { evaluate: "document.title" }, ], }), }); const data = await response.json(); console.log(data.scenario);
<?php $ch = curl_init("https://scrapingbot.io/api/v1/scrape"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "x-api-key: YOUR_API_KEY", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "url" => "https://shop.example.com", "js_scenario" => [ ["click" => "#accept-cookies"], ["fill" => ["input[name=q]", "running shoes"]], ["click" => "button[type=submit]"], ["wait_for" => ".results"], ["scroll_y" => 1500], ["evaluate" => "document.title"], ], ]), ]); $data = json_decode(curl_exec($ch), true); print_r($data["scenario"]);
Response
{ "success": true, "url": "https://shop.example.com", "html": "<!DOCTYPE html><html lang=\"en\">…", "status": 200, "duration": "6.12", "scenario": { … }, "credits_used": 5, "job_id": "c3f1a9e2-0d5b-4c8e-9a71-5b2f0e6d4a10" }
Screenshots
Capture the page as a PNG alongside its HTML. The image comes back base64-encoded in the screenshot field; decode it and save it as a .png file.
- screenshotstring
truecaptures the visible viewport;full_pagecaptures the whole scrollable page. Turns on rendering (5 credits).
Combine it with a js_scenario to see exactly what the browser saw after your actions, which makes scenarios much easier to debug.
Request
curl -G "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ --data-urlencode "url=https://example.com" \ --data-urlencode "screenshot=full_page" \ | jq -r ".screenshot" | base64 --decode > page.png
import requests import base64 response = requests.get( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, params={"url": "https://example.com", "screenshot": "full_page"}, timeout=60, ) data = response.json() with open("page.png", "wb") as f: f.write(base64.b64decode(data["screenshot"]))
import { writeFileSync } from "node:fs"; const params = new URLSearchParams({ url: "https://example.com", screenshot: "full_page" }); const response = await fetch(`https://scrapingbot.io/api/v1/scrape?${params}`, { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); writeFileSync("page.png", Buffer.from(data.screenshot, "base64"));
<?php $query = http_build_query(["url" => "https://example.com", "screenshot" => "full_page"]); $ch = curl_init("https://scrapingbot.io/api/v1/scrape?" . $query); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); file_put_contents("page.png", base64_decode($data["screenshot"]));
Response
{ "success": true, "url": "https://example.com", "html": "<!DOCTYPE html><html lang=\"en\">…", "status": 200, "duration": "1.85", "screenshot": "iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAIAAABnsVYUAAAQ…", "scenario": {}, "credits_used": 5, "job_id": "d7e0c4b1-8f2a-4e6d-b3c9-1a5f7e2d9c08" }
AI extraction
Skip the parsing: describe what you want and get structured JSON back in ai_result. The AI reads the page's text content (scripts and styles removed, up to 50,000 characters). It adds 5 credits to the scrape.
- ai_querystringPlain-English description, for example
product name, price and rating. Key names are chosen for you. - ai_schemaJSON schemaThe exact fields you want, as an object (or a JSON string) with
properties. Each property can have atypeand adescription. Fields that aren't found come back asnull,falseor[]. Takes precedence overai_query.
In the response
ai_result: the extracted object.ai_queryorai_schema: what you asked for, echoed back.ai_error: present if extraction failed (for example a schema without properties). The scrape itself still succeeds, returns the HTML and is charged as requested.
Request
curl -X POST "https://scrapingbot.io/api/v1/scrape" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://shop.example.com/p/123", "render_js": true, "ai_schema": { "type": "object", "properties": { "name": {"type": "string", "description": "Product name"}, "price": {"type": "number"}, "in_stock": {"type": "boolean"} } } }'
import requests response = requests.post( "https://scrapingbot.io/api/v1/scrape", headers={"x-api-key": "YOUR_API_KEY"}, json={ "url": "https://shop.example.com/p/123", "render_js": True, "ai_schema": { "type": "object", "properties": { "name": {"type": "string", "description": "Product name"}, "price": {"type": "number"}, "in_stock": {"type": "boolean"}, }, }, }, timeout=60, ) data = response.json() print(data["ai_result"])
const response = await fetch("https://scrapingbot.io/api/v1/scrape", { method: "POST", headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://shop.example.com/p/123", render_js: true, ai_schema: { type: "object", properties: { name: { type: "string", description: "Product name" }, price: { type: "number" }, in_stock: { type: "boolean" }, }, }, }), }); const data = await response.json(); console.log(data.ai_result);
<?php $ch = curl_init("https://scrapingbot.io/api/v1/scrape"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "x-api-key: YOUR_API_KEY", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "url" => "https://shop.example.com/p/123", "render_js" => true, "ai_schema" => [ "type" => "object", "properties" => [ "name" => [ "type" => "string", "description" => "Product name", ], "price" => ["type" => "number"], "in_stock" => ["type" => "boolean"], ], ], ]), ]); $data = json_decode(curl_exec($ch), true); print_r($data["ai_result"]);
Response
{ "success": true, "url": "https://shop.example.com/p/123", "html": "<!DOCTYPE html><html lang=\"en\">…", "status": 200, "duration": "4.87", "ai_result": { "name": "Trail Runner 2", "price": 129, "in_stock": true }, "ai_schema": { … }, "credits_used": 10, "job_id": "e91b2f4c-6a3d-4b8e-a0c5-7d2e9f1b3a64" }
Response & faults
The HTTP status of the response mirrors the target page's status: a page that answers 404 gives you an HTTP 404 with fault: "user". If your HTTP client throws on non-2xx statuses, read the JSON body anyway.
Success
- successboolean
true. - urlstringThe URL you asked for.
- htmlstringThe page HTML, rendered if rendering was on.
- statusintegerThe status the target page answered with.
- durationstringSeconds the request took, for example
"1.42". - screenshotstringBase64 PNG, when requested.
- scenarioobjectRun report, when a
js_scenarioor screenshot was requested. - ai_result, ai_query, ai_schema, ai_errorWith AI extraction.
- credits_usedintegerWhat this request cost.
- job_idstringLook the result up again for 24 hours with GET /job.
Failure
Failed scrapes keep the same shape without the page: success, url, status, fault (user, system or timeout), error, duration, credits_used and job_id. Requests rejected before they run return only success, status and error.
Scrape responses include an x-request-id header. Send your own x-request-id and it's echoed back, so you can tie our logs to yours.
Examples
{ "success": true, "url": "https://example.com", "html": "<!doctype html><html lang=en><head><meta charset=utf-8><ti…", "status": 200, "duration": "0.79", "credits_used": 1, "job_id": "b8a5d61d-6148-4fb9-b2e6-84a268200472" }
Job lookup
Every scrape is recorded as a job, so you can fetch its result again with the job_id from the response, for example after a dropped connection.
- Results are kept for 24 hours; after that the job still reports its status, without the result.
- You can only see jobs created with your own account.
- Lookups are free.
- statusstring
completedorfailed. - resultobjectFor completed jobs:
html,status(the target's status),fault,scenario,screenshotUrl(the base64 screenshot, if any) andresolvedUrl. - errorstringFor failed jobs, why it failed.
- created_at, started_at, completed_attimestampsISO 8601.
Request
curl "https://scrapingbot.io/api/v1/job/b8a5d61d-6148-4fb9-b2e6-84a268200472" \ -H "x-api-key: YOUR_API_KEY"
import requests response = requests.get( "https://scrapingbot.io/api/v1/job/b8a5d61d-6148-4fb9-b2e6-84a268200472", headers={"x-api-key": "YOUR_API_KEY"}, timeout=60, ) data = response.json() print(data["status"])
const response = await fetch("https://scrapingbot.io/api/v1/job/b8a5d61d-6148-4fb9-b2e6-84a268200472", { headers: { "x-api-key": "YOUR_API_KEY" }, }); const data = await response.json(); console.log(data.status);
<?php $ch = curl_init("https://scrapingbot.io/api/v1/job/b8a5d61d-6148-4fb9-b2e6-84a268200472"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: YOUR_API_KEY"], ]); $data = json_decode(curl_exec($ch), true); print_r($data["status"]);
Response
{ "success": true, "job_id": "b8a5d61d-6148-4fb9-b2e6-84a268200472", "status": "completed", "created_at": "2026-10-02T19:52:11.204Z", "started_at": "2026-10-02T19:52:11.204Z", "completed_at": "2026-10-02T19:52:12.001Z", "result": { "html": "<!doctype html><html lang=en>…", "statusCode": 200, "status": 200, "fault": "none", "screenshotUrl": null, "scenario": null, "resolvedUrl": "https://example.com" } }
Stuck on something?
Try requests in the dashboard playgrounds, check every call in your usage log, or write to [email protected]. A real developer answers.