For AI agents: a documentation index is available at /llms.txt
Skip to main content

Connect Playwright

Run your Playwright scripts against managed browsers by replacing chromium.launch() with chromium.connectOverCDP(). Locators, assertions, and waits work exactly as they do locally.

This page is for code you already have. If you're writing automation from scratch, the BAP SDK is usually the better starting point.

Steps

  1. Get Your API Token

    Sign up for a free account, then copy your API token from the account dashboard. Every connection carries it as a ?token= query parameter.

  2. Install Playwright

    npm install playwright-core

    playwright-core skips the browser downloads that playwright performs on install. You don't need local binaries when the browser runs on Browserless.

  3. Swap launch() for connectOverCDP()

    import { chromium } from "playwright-core";

    const TOKEN = "YOUR_API_TOKEN_HERE";

    // Before: a browser process on this machine.
    // const browser = await chromium.launch();

    // After: a managed browser reached over a CDP WebSocket.
    const browser = await chromium.connectOverCDP(
    `wss://production-sfo.browserless.io?token=${TOKEN}`
    );

    Use connectOverCDP, not connect. The default Browserless endpoint speaks the Chrome DevTools Protocol, which is what connectOverCDP expects. connect speaks Playwright's own server protocol and needs a different path, covered in Playwright native protocol below.

  4. Run a Complete Script

    import { chromium } from "playwright-core";

    const TOKEN = "YOUR_API_TOKEN_HERE";

    const browser = await chromium.connectOverCDP(
    `wss://production-sfo.browserless.io?token=${TOKEN}`
    );

    try {
    const page = await browser.newPage();
    await page.goto("https://example.com");

    const title = await page.title();
    const heading = await page.textContent("h1");

    console.log({ title, heading });
    } finally {
    // Release the session even if something above threw.
    await browser.close();
    }

    Output

    { title: 'Example Domain', heading: 'Example Domain' }

Java, C#, and Go follow the same pattern. The Browsers as a Service quickstart has a runnable example in each.

Configuring the browser

Options you used to pass to launch() move onto the connection URL, since the browser starts before your code connects to it.

const browser = await chromium.connectOverCDP(
`wss://production-sfo.browserless.io?token=${TOKEN}&blockAds=true&timeout=60000`
);

See launch parameters for the full list, including proxies, stealth, and Chrome flags.

Playwright native protocol

connectOverCDP covers most scripts, but a few Playwright features need Playwright's own protocol rather than CDP: page.route() network interception, APIRequestContext, and any browser other than Chromium. For those, switch to connect() against a /playwright path:

import { firefox } from "playwright-core";

const browser = await firefox.connect(
`wss://production-sfo.browserless.io/firefox/playwright?token=${TOKEN}`
);

/chromium/playwright, /chrome/playwright, /firefox/playwright, /webkit/playwright, and /edge/playwright are all available. The tradeoff is version coupling: native mode pins you to the Playwright version the endpoint runs, while CDP mode is far more tolerant of client version drift. See connect vs connectOverCDP for the feature-by-feature breakdown.

The BAP alternative

BAP is our own SDK. Its Python API borrows Playwright's method names, and its TypeScript API borrows Puppeteer's, so the script above ports with small changes:

import Browserless from "@browserless.io/bap-ts";

const TOKEN = "YOUR_API_TOKEN_HERE";

// connect() opens no socket. The WebSocket opens on newPage().
const browser = Browserless.connect({
browserWSEndpoint: "wss://production-sfo.browserless.io/chromium/bql",
token: TOKEN,
});

try {
const page = await browser.newPage();
await page.goto("https://example.com");

const title = await page.title();
const { text } = await page.text({ selector: "h1" });

console.log({ title, heading: text });
} finally {
await browser.close();
}

Output

{ title: 'Example Domain', heading: 'Example Domain' }

It isn't a drop-in replacement. BAP runs over BrowserQL rather than a live CDP session, so frames, browser contexts, and direct input control have no equivalent, and page.route() interception isn't part of it either. What you get in exchange is solve(), proxy(), markdown(), and liveURL() as methods instead of plumbing.

Keep this page's connectOverCDP() approach when your script needs those APIs, or when you're running Playwright Test suites. See Migrate to BAP for the full comparison.

FAQ & Troubleshooting

Why does connect() fail against the default endpoint?

connect() expects Playwright's server protocol, and the default endpoint serves CDP. Either use connectOverCDP() on the default endpoint, or keep connect() and point it at a /playwright path such as /chromium/playwright.

Why is page.route() not intercepting anything?

Route interception is a Playwright-protocol feature and isn't available over CDP. Connect through /chromium/playwright with connect() to use it, or block resources with the rejectResourceTypes request option instead.

Can I run Playwright test runner suites against Browserless?

Yes. Create the connection in a worker-scoped fixture so the tests in each worker share one remote browser. Global setup runs in its own process and can't hand a live browser to the workers, so it won't do. Each parallel worker opens its own session and counts against your plan's concurrency limit. Note that launch-only options in playwright.config.js have no effect on a remote browser, since the options travel on the connection URL instead.

Do I need playwright install?

No. That command downloads local browser binaries. A remote connection never touches them, so skipping it keeps installs and CI images small.

Next steps

Was this page helpful?