Scrape.ly Browser
Run Playwright and Puppeteer on the hosted anti-bot browser: turn it on per Actor, set its location, and see what it costs.
The Scrape.ly browser is a hosted Chromium your Actor's browsers can run on instead of inside the run container. It has anti-bot fingerprinting, solves Cloudflare Turnstile challenges, and routes traffic through its own residential proxy.
It's off by default. You turn it on per Actor, rebuild, and your Playwright or Puppeteer code connects to it without changes.
Turn it on
1. Enable it for the Actor
In the dashboard, open the Actor and go to Settings → Scrapely Browser. Turn on Use Scrapely browser and confirm. Only the Actor's owner can change it.
Or with the API:
curl -X PUT "https://api.scrape.ly/v2/actors/<actorId>/browser-patch" \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"browserPatchEnabled": true}'2. Rebuild
The setting is applied when the Actor is built, so start a new build: scrapely push, Builds → Build, or the build API. Existing builds keep working as they were.
3. Run it
Runs of the new build connect every browser launch to the Scrape.ly browser. The build log shows which mode a build uses:
[Scrapely Build] Browser patch: on (remote browser)On and off at a glance
| Off (default) | On | |
|---|---|---|
| Where browsers run | In the run container, from the browsers in your image | On the Scrape.ly browser, over CDP |
| Browser downloads in the build | Kept | Removed: playwright install, pyppeteer-install, Puppeteer's postinstall download |
| Proxy for browser traffic | Whatever you configure | The Scrape.ly browser's own proxy. Your proxy URLs are ignored for browsers |
| Firefox, WebKit, Camoufox | Launch as requested | Redirected to the Scrape.ly browser (Chromium) |
| Billing | Compute units | Compute units plus browser credits |
| Build object | browserPatched: false | browserPatched: true |
| Run environment | SCRAPELY_BROWSER_PATCH=0 | Not set |
Builds made before this setting existed have browserPatched: null and count as on. Rebuild with the setting off to move them to local browsers.
What changes in the build
With the setting on, the build:
- Removes browser binary downloads from the Dockerfile:
playwright install,pyppeteer-install,PLAYWRIGHT_BROWSERS_PATHdownloads, Puppeteer's postinstall download, and Apify'scheck-playwright-version.mjsguard. The Playwright and Puppeteer packages and OS-package browsers (apt-get install chromium) stay. - Replaces each
playwright installwithmkdir -pof the browsers directory (PLAYWRIGHT_BROWSERS_PATH, else Playwright's default), so later steps that touch it, such aschmod -R a+rX /ms-playwright, still work. - Makes sure the image loads the redirect.
scrapely/actor-node*andscrapely/actor-python*images already do, throughNODE_OPTIONSandsitecustomize. Other images get a small preload for Node.js or Python and nothing else. - Never edits your stored source.
It works the same whether the account-level Transform actors setting is on or off.
What gets redirected
On the platform, the SDK patches every launch path so it connects to the browser in CDP_URL: Playwright uses chromium.connectOverCDP(), Puppeteer uses puppeteer.connect({ browserWSEndpoint }). Actors that never import the SDK are covered too, through the image preload.
| Launch path | JavaScript | Python |
|---|---|---|
| Crawlee crawlers | PlaywrightCrawler, PuppeteerCrawler, other BrowserCrawler subclasses | PlaywrightCrawler |
| Raw launches | chromium.launch(), puppeteer.launch() | chromium.launch() and launch_persistent_context() (sync and async), pyppeteer launch() |
| Other browsers | Firefox, WebKit, and Camoufox launchers go to Chromium | Firefox, WebKit, and Camoufox go to Chromium |
Choose the location and fingerprint
The Scrape.ly browser never takes a proxy URL. Pick where its traffic exits, and how it looks, with a browser configuration:
import { Actor, PlaywrightCrawler } from 'scrapely';
await Actor.main(async () => {
const browserConfiguration = await Actor.createBrowserConfiguration({
countryCode: 'US',
state: 'California',
os: 'windows',
});
const crawler = new PlaywrightCrawler({
browserConfiguration,
async requestHandler({ page }) {
await Actor.pushData({ title: await page.title() });
},
});
await crawler.run(['https://example.com']);
});| Option (JS / Python) | CDP URL parameter | Format | Default |
|---|---|---|---|
countryCode / country_code | proxy_country | ISO 3166-1 alpha-2, e.g. US | The run default, US |
state | proxy_state | Region name, e.g. California (sent as california) | None |
city | proxy_city | City name, e.g. New York (sent as new.york) | None |
os | os | windows, macos, or android | Chosen by the browser |
timezone | timezone | IANA name, up to 64 characters | The exit country's zone |
language | language | Tags without q-values, e.g. de-CH,de | The exit country's language |
adblock | adblock | Boolean | false |
solveCloudflare / solve_cloudflare | solve_cloudflare | Boolean | true |
- Input aliases. Pass your Actor input straight in:
browserCountry,browserState, andbrowserCityfillcountryCode,state, andcity. Explicit options win. - Precedence. The location comes from the browser configuration, then the country and subdivision of a platform
ProxyConfigurationon the crawler, then the run default. - Validation. Invalid values throw (JavaScript) or raise
ValueError(Python) when you create the configuration, before any browser starts. - Sessions. Proxy groups and session IDs don't apply. Each browser connection is a fresh browser session, and the first page that launches a browser fixes its location.
- Same code everywhere. The option is accepted locally and in builds with the setting off, and ignored there.
Connect over CDP yourself
Every run gets CDP_URL, whatever the setting. Connecting to it directly uses the Scrape.ly browser even in a build with the setting off, and bills browser credits:
import { chromium } from 'playwright';
const browser = await chromium.connectOverCDP(process.env.CDP_URL);To set the location on a direct connection, add the CDP URL parameters from the table above to the URL.
- The URL identifies the run. The browser accepts it only while the run is
READY,RUNNING,ABORTING, orTIMING-OUT, and only if the account running the Actor has a positive balance. - It carries the run's default exit country as
proxy_country(US).
Pricing
The Scrape.ly browser is billed in browser credits, on top of the run's compute units.
- Who pays: the account that runs the Actor. For a Store Actor that's the user, not the developer.
- What counts: session time in 30-second blocks (rounded up, at least one block) and the browser's proxy traffic per MB (rounded up, at least 1 MB). That traffic is part of the credits and is never billed again as proxy GB.
- Price: your price per credit is under Billing → Pricing.
- Spending cap: browser credits count toward the run's
maxTotalChargeUsd. When the cap is reached, the run is stopped.
Where to see it:
| Place | Shows |
|---|---|
| Run page → cost breakdown | Browser credits for the run |
| Billing → Current period | Browser credits, with the session time and proxy traffic they include |
| Run object in the API | usage.BROWSER_CREDITS, usage.BROWSER_SECONDS, usageUsd.BROWSER_CREDITS |
GET /v2/users/me/usage/monthly | BROWSER_CREDITS in monthlyServiceUsage |
Local runs
scrapely runand other local runs have noCDP_URL, so browsers launch on your machine, as in plain Crawlee. The browser configuration is ignored.setCdpUrl(url)(JavaScript) orset_cdp_url(url)(Python) points the SDK at a CDP endpoint you run yourself. The Scrape.ly browser only accepts connections from platform runs.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Browsers still launch in the container | The build was made before you turned the setting on | Rebuild, then check the build log or the Scrapely browser badge |
cdpUrl is required. Local browser launch is disabled | A build with the setting on started a browser without CDP_URL | Don't unset CDP_URL or clear it with setCdpUrl(undefined) in code that runs on the platform |
Custom proxies are ignored when using the Scrapely remote browser | A browser crawler or launch got your own proxy | Use a browser configuration; keep your proxy for HTTP crawlers |
Subdivision "XX-YY" has no Scrapely browser mapping | A proxy subdivisionCode the browser can't map | Set state in the browser configuration |
Unknown option browserConfiguration | It was passed to an HTTP crawler | Only browser crawlers accept it |
| Browser fails to launch after turning the setting off | The image has no browser binaries | Use a browser image such as scrapely/actor-node-playwright-chrome or install the browsers in your Dockerfile |
Related: Browser patch API, JavaScript environment, Python environment, Proxy.