Send a URL; get the page, structured data, or what your browser steps collected. Requests that hit an anti-bot wall are never charged.
Create a key in the dashboard, then:
curl "https://stealthasf.bunnybag.space/v1/scrape" \
-H "x-api-key: sasf_live_…" \
-H "content-type: application/json" \
-d '{"url": "https://quotes.toscrape.com/", "engine": "auto", "extract": "links"}'
Authenticate with x-api-key: <key> or Authorization: Bearer <key>.
Browser requests can take up to ~2 minutes — set your client timeout to 180 s.
POST /v1/scrape| field | type | meaning |
|---|---|---|
| url | string, required | http(s) URL, up to 2048 chars. Internal/private addresses are refused. |
| engine | auto · http · browser · stealth | auto (default) starts on plain HTTP and steps up to the stealth browser only when the site answers with an anti-bot wall; it also remembers sites that needed stealth. http: no browser. browser: real browser for JavaScript-built pages. stealth: anti-detect browser that clears Cloudflare Turnstile. |
| extract | links · text · meta · emails · images · headings · table | Return structured data instead of parsing HTML yourself. |
| steps | array, up to 40 | Browser actions run in order on the page (see below). Forces a browser engine. |
| randomize | {delays, motion, order} | Human-like pauses, mouse and scroll between steps. |
| repeat, repeat_gap_ms | int, int (≤ 60000) | Run the steps N times in one browser session with a pause between (e.g. read a price every 10 s). Stops after ~14 min. |
Each step is a one-key object. Selectors start with css: or xpath:; append
::text or ::attr(href) to read text or an attribute.
| step | example |
|---|---|
| crawl | {"crawl": {"links": "css:a.item", "next_page": "css:a.next", "fields": {"title": "css:h1::text"}, "max_items": 500}} — opens every detail link on each list page and extracts fields there, following next_page. One Cloudflare clearance for the whole crawl. Without links, extracts item_selector rows from the list pages. |
| scrape | {"scrape": {"item_selector": "css:.row", "fields": {"name": "css:.name::text"}}} |
| click · hover · scroll · wait_for | {"click": "css:button.more"} |
| type | {"type": {"selector": "css:input[name=q]", "text": "boots"}} |
| press_key | {"press_key": "Enter"} |
| goto · wait | {"goto": "https://site/page/2"} · {"wait": 1500} |
| find_and_click | {"find_and_click": {"text": "Load more"}} — scrolls until the text appears, then clicks. |
| collect_links | {"collect_links": {"selector": "css:a.result"}} |
| capture | {"capture": {"name": "price", "selector": "css:.price"}} — stores a value; use it later as {{price}}. |
| js_eval | {"js_eval": {"name": "items", "code": "return (await fetch('/api/items')).json()"}} — runs in the cleared page with its cookies. |
| if_exists · try | {"if_exists": {"selector": "css:.cookie-ok", "then": [{"click": "css:.cookie-ok"}]}} · {"try": {"steps": [...]}} (failures inside are ignored) |
{
"ok": true,
"job_id": "cm…",
"engine": "stealth", // the tier that produced the result
"status": 200, // the target's HTTP status
"html": "<!doctype html>…",
"data": { "kind": "table", "columns": [...], "rows": [[...]] }, // with "extract"
"records": [ {"type": "crawl", "url": "…", "data": {...}} ], // with "steps"
"discovered_api": { "kind": "json_api", "url": "…", "method": "GET" }, // when the page called its own data API
"usage": { "engine": "stealth", "bytes": 3145728, "proxy_mb": 3.1, "ms": 53210 },
"credits_charged": 45,
"balance": 249955
}
| status | meaning | charged |
|---|---|---|
| 400 | Invalid body, URL, engine, extract or step | no |
| 401 | Missing or invalid API key | no |
| 402 | Not enough credits (balance included) | no |
| 422 | The site's anti-bot blocked the request (job_id, balance included) | no |
| 429 | Rate limit (120 requests/min per IP), or a free-plan account already has a request running | no |
| 502 | The target could not be reached, or an engine error | no |
| what | credits |
|---|---|
| http request | 1 (+1 per 2 MB of response) |
| browser request | 5 (+10 per MB of transfer beyond the first MB) |
| stealth request | 25 (+10 per MB of transfer beyond the first MB) |
| solved captcha | +25 |
| blocked request | 0 |
With engine: auto you pay only for the tier that succeeded. Images, video and fonts are not
downloaded unless you ask for screenshots, so transfer stays small.