Playwright
Playwright attaches over CDP with connect_over_cdp() in Python and
connectOverCDP() in Node.js. Get the URL from /connect, hand it over, and use
Playwright normally.
Over HTTP
Section titled “Over HTTP”import httpxfrom playwright.sync_api import sync_playwright
API = "https://browser.rayobyte.com"KEY = "rb_live_YOUR_KEY"
resp = httpx.get( f"{API}/connect", params={"os": "windows", "proxy": PROXY}, headers={"x-api-key": KEY}, timeout=120,)if resp.status_code != 200: raise SystemExit(f"{resp.status_code}: {resp.text}")cdp_url = resp.text.strip()session_id = resp.headers["x-session-id"]
try: with sync_playwright() as p: browser = p.chromium.connect_over_cdp(cdp_url) context = browser.contexts[0] page = context.pages[0] if context.pages else context.new_page() page.goto("https://quotes.toscrape.com/") print(page.title()) # Quotes to Scrapefinally: httpx.post( f"{API}/api/browser/close", json={"browserId": session_id}, headers={"x-api-key": KEY}, timeout=30, )// Save as playwright.mjs and run: node playwright.mjsimport { chromium } from 'playwright';
const API = 'https://browser.rayobyte.com';const KEY = 'rb_live_YOUR_KEY';
const url = new URL('/connect', API);url.searchParams.set('os', 'windows');url.searchParams.set('proxy', PROXY);
const resp = await fetch(url, { headers: { 'x-api-key': KEY } });if (!resp.ok) throw new Error(`${resp.status}: ${await resp.text()}`);const cdpUrl = (await resp.text()).trim();const sessionId = resp.headers.get('x-session-id');
try { const browser = await chromium.connectOverCDP(cdpUrl); const context = browser.contexts()[0]; const page = context.pages()[0] ?? (await context.newPage()); await page.goto('https://quotes.toscrape.com/'); console.log(await page.title()); // Quotes to Scrape await browser.close();} finally { await fetch(`${API}/api/browser/close`, { method: 'POST', headers: { 'x-api-key': KEY, 'content-type': 'application/json' }, body: JSON.stringify({ browserId: sessionId }), });}browser.close() does not stop a browser Playwright attached to over CDP. It
disconnects Playwright and the session keeps running, with its tabs and cookies,
until the lifetime ends at 7,200 seconds by default. The close call in finally
is what ends it. To keep the browser for a later run instead, skip that call,
store the x-session-id value and
reconnect.
Use the context that is already open
Section titled “Use the context that is already open”Take browser.contexts[0] rather than calling new_context(). A new context
applies Playwright’s default 1280×720 viewport emulation, so the page then
reports a screen and window size that disagree with each other and with the
session’s profile: outerWidth ends up smaller than innerWidth, which no
real Chrome window does.
If you need a second context, pass no_viewport=True (noViewport: true in
Node.js) so it keeps the real window size.
Through the SDK
Section titled “Through the SDK”from rayobrowse import Rayobrowsefrom playwright.sync_api import sync_playwright
client = Rayobrowse(endpoint="https://browser.rayobyte.com", api_key="rb_live_YOUR_KEY")ws_url = client.connect_url(os="windows", proxy=PROXY)
try: with sync_playwright() as p: browser = p.chromium.connect_over_cdp(ws_url) page = browser.contexts[0].pages[0] page.goto("https://quotes.toscrape.com/") print(page.title())finally: client.close()// Save as sdk.mjs and run: node sdk.mjsimport { Rayobrowse } from 'rayobrowse';import { chromium } from 'playwright';
const client = new Rayobrowse({ endpoint: 'https://browser.rayobyte.com', apiKey: 'rb_live_YOUR_KEY',});const wsUrl = await client.connectUrl({ os: 'windows', proxy: PROXY });
try { const browser = await chromium.connectOverCDP(wsUrl); const page = browser.contexts()[0].pages()[0]; await page.goto('https://quotes.toscrape.com/'); console.log(await page.title()); await browser.close();} finally { await client.close();}Don’t set the User-Agent yourself
Section titled “Don’t set the User-Agent yourself”context.set_extra_http_headers({"User-Agent": ...}) and Playwright’s
user_agent context option both override the one the fingerprint set. The header
then disagrees with navigator.userAgent, the client hints and the platform,
which is the mismatch detection systems check for first.
Leave the headers alone.
Was this page helpful?
Thanks — that helps us fix it.