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
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.Install Playwright
- JavaScript
- Python
npm install playwright-coreplaywright-coreskips the browser downloads thatplaywrightperforms on install. You don't need local binaries when the browser runs on Browserless.pip install playwrightSkip
playwright install. That command downloads local browser binaries, which a remote connection never uses.Swap launch() for connectOverCDP()
- JavaScript
- Python
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}`
);from playwright.sync_api import sync_playwright
TOKEN = "YOUR_API_TOKEN_HERE"
with sync_playwright() as p:
# Before: a browser process on this machine.
# browser = p.chromium.launch()
# After: a managed browser reached over a CDP WebSocket.
browser = p.chromium.connect_over_cdp(
f"wss://production-sfo.browserless.io?token={TOKEN}"
)Use
connectOverCDP, notconnect. The default Browserless endpoint speaks the Chrome DevTools Protocol, which is whatconnectOverCDPexpects.connectspeaks Playwright's own server protocol and needs a different path, covered in Playwright native protocol below.Run a Complete Script
- JavaScript
- Python
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' }from playwright.sync_api import sync_playwright
TOKEN = "YOUR_API_TOKEN_HERE"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(
f"wss://production-sfo.browserless.io?token={TOKEN}"
)
try:
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
print(page.text_content("h1"))
finally:
# Release the session even if something above raised.
browser.close()Output
Example Domain
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:
- TypeScript
- Python
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' }
import bap.sync_api as bap
TOKEN = "YOUR_API_TOKEN_HERE"
# The context manager closes the page and the browser when the block exits.
with bap.Browserless.connect(
browser_ws_endpoint="wss://production-sfo.browserless.io/chromium/bql",
token=TOKEN,
) as browser:
with browser.page() as page:
page.goto("https://example.com")
print(page.title())
print(page.text(selector="h1")["text"])
Output
Example Domain
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.