POST /scrape loads one page in a browser that is already running and returns it as cleaned HTML, Markdown, links or a screenshot. The browser runs behind a residential proxy of the region you picked in the dashboard, with a desktop fingerprint, solves the challenge of a blocked page and keeps nothing between requests.
Use the full flow instead when pages must share a login, when you need a specific country or city rather than the region, when you bring your own proxy or fingerprint, or when you need extensions or a persistent profile.
To have a coding agent set this up in your project, give it the setup prompt.
Scrape a page
Set your API token and base URL. The endpoint is available on on-demand plans.- curl
- Python (async)
- JavaScript
Examples
Request parameters
The same body works onPOST /scrape and on POST /profiles/{internal_uuid}/scrape.
wait_for with a selector for the content you need is more reliable than networkidle, which can time out on pages with background traffic.
A solve adds 10 to 60 seconds within the 120-second budget. Each solve is charged at your plan’s CAPTCHA price, whether or not the page opens afterwards. With auto_captcha_solve: false a blocked page comes back as it is, with its status. A widget on a page that opened normally is never solved by this endpoint; use CAPTCHA solving in your own browser for those.
Actions
The first failing action ends the request with
action_failed; the response data carries the page the browser was left on. A block page that a click leads to is solved like the first one.
Read the response
data.status is the target site’s HTTP status after redirects and solves, so a 200 from Surfsky can carry a 403 from the site. Each requested format adds its own key: content for html, raw_html, markdown, links (absolute URLs, deduplicated), screenshot (Base64) and cookies (the jar after the scrape, accepted as cookies of the next request). A response to an error status with an empty body has that status and empty content.
A request that could not finish answers HTTP 400 with success: false and a code:
on_demand_plan_required (403) means the token’s plan is not on-demand, insufficient_balance (402) an empty balance, parallel_browsers_limit_reached (429) that every browser slot of the account is in use. A request in flight holds a slot like a running browser.
Billing
A request is one session, billed in 30-second steps of browser time. For example, a 20-second scrape costs 30 seconds and a 70-second one costs 90. Proxy traffic is charged per GB at your plan’s premium rate, and each CAPTCHA solve at its price. A page the site refused, an unreachable site and a browser that failed on our side are not charged.When to run your own browser
Start a browser, scrape through it and stop it.POST /profiles/{internal_uuid}/scrape takes the body above, and also an array of bodies: the pages load one after another in one browser, and data comes back as an array in that order, with error, code and status_code in place of a failed page. The 120 seconds cover the whole batch.
client.profiles.scrape(session, url, ...) inside client.session(), which stops the browser when the scope exits. Stop the browser yourself after direct HTTP requests, or it stops after the inactivity timeout. Do not mix scrape calls with CDP automation in the same browser: the scraper takes over the page and closes extra tabs. For input sequences longer than ten actions, drive the browser through the SDK’s human emulation methods instead.