Skip to content
Build Actors

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:

.bash
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:

.text
[Scrapely Build] Browser patch: on (remote browser)

On and off at a glance

Off (default)On
Where browsers runIn the run container, from the browsers in your imageOn the Scrape.ly browser, over CDP
Browser downloads in the buildKeptRemoved: playwright install, pyppeteer-install, Puppeteer's postinstall download
Proxy for browser trafficWhatever you configureThe Scrape.ly browser's own proxy. Your proxy URLs are ignored for browsers
Firefox, WebKit, CamoufoxLaunch as requestedRedirected to the Scrape.ly browser (Chromium)
BillingCompute unitsCompute units plus browser credits
Build objectbrowserPatched: falsebrowserPatched: true
Run environmentSCRAPELY_BROWSER_PATCH=0Not 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_PATH downloads, Puppeteer's postinstall download, and Apify's check-playwright-version.mjs guard. The Playwright and Puppeteer packages and OS-package browsers (apt-get install chromium) stay.
  • Replaces each playwright install with mkdir -p of the browsers directory (PLAYWRIGHT_BROWSERS_PATH, else Playwright's default), so later steps that touch it, such as chmod -R a+rX /ms-playwright, still work.
  • Makes sure the image loads the redirect. scrapely/actor-node* and scrapely/actor-python* images already do, through NODE_OPTIONS and sitecustomize. 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 pathJavaScriptPython
Crawlee crawlersPlaywrightCrawler, PuppeteerCrawler, other BrowserCrawler subclassesPlaywrightCrawler
Raw launcheschromium.launch(), puppeteer.launch()chromium.launch() and launch_persistent_context() (sync and async), pyppeteer launch()
Other browsersFirefox, WebKit, and Camoufox launchers go to ChromiumFirefox, 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 parameterFormatDefault
countryCode / country_codeproxy_countryISO 3166-1 alpha-2, e.g. USThe run default, US
stateproxy_stateRegion name, e.g. California (sent as california)None
cityproxy_cityCity name, e.g. New York (sent as new.york)None
ososwindows, macos, or androidChosen by the browser
timezonetimezoneIANA name, up to 64 charactersThe exit country's zone
languagelanguageTags without q-values, e.g. de-CH,deThe exit country's language
adblockadblockBooleanfalse
solveCloudflare / solve_cloudflaresolve_cloudflareBooleantrue
  • Input aliases. Pass your Actor input straight in: browserCountry, browserState, and browserCity fill countryCode, state, and city. Explicit options win.
  • Precedence. The location comes from the browser configuration, then the country and subdivision of a platform ProxyConfiguration on 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, or TIMING-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:

PlaceShows
Run page → cost breakdownBrowser credits for the run
Billing → Current periodBrowser credits, with the session time and proxy traffic they include
Run object in the APIusage.BROWSER_CREDITS, usage.BROWSER_SECONDS, usageUsd.BROWSER_CREDITS
GET /v2/users/me/usage/monthlyBROWSER_CREDITS in monthlyServiceUsage

Local runs

  • scrapely run and other local runs have no CDP_URL, so browsers launch on your machine, as in plain Crawlee. The browser configuration is ignored.
  • setCdpUrl(url) (JavaScript) or set_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

SymptomCauseFix
Browsers still launch in the containerThe build was made before you turned the setting onRebuild, then check the build log or the Scrapely browser badge
cdpUrl is required. Local browser launch is disabledA build with the setting on started a browser without CDP_URLDon'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 browserA browser crawler or launch got your own proxyUse a browser configuration; keep your proxy for HTTP crawlers
Subdivision "XX-YY" has no Scrapely browser mappingA proxy subdivisionCode the browser can't mapSet state in the browser configuration
Unknown option browserConfigurationIt was passed to an HTTP crawlerOnly browser crawlers accept it
Browser fails to launch after turning the setting offThe image has no browser binariesUse 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.