Skip to navigation

CDP Connection

View as Markdown

How to connect to a Chaser session, what works, and what to avoid.

The endpoint

Every ready session exposes a standard Chrome DevTools Protocol WebSocket:

wss://cdp.chaser.sh/sessions/{session_id}

This is the same protocol your local browser uses. Any CDP-compatible client works.

import { chromium } from "patchright";
const browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0];
const page = context.pages()[0] ?? await context.newPage();
await page.goto("https://www.cloudflare.com");
console.log(await page.title());

connectOverCDP attaches to the existing browser process. Do not call chromium.launch() — that starts a local browser, which is not what you want.

Connecting with Puppeteer

import puppeteer from "puppeteer-core";
const browser = await puppeteer.connect({ browserWSEndpoint: cdpUrl });
const [page] = await browser.pages();
await page.goto("https://www.cloudflare.com");
console.log(await page.title());

Connecting with raw CDP

Any WebSocket client that speaks CDP works. Here’s a minimal example using the ws package:

import WebSocket from "ws";
const ws = new WebSocket(cdpUrl);
ws.on("open", () => {
// Navigate
ws.send(JSON.stringify({
id: 1,
method: "Page.navigate",
params: { url: "https://www.cloudflare.com" },
}));
});
ws.on("message", (data) => {
const msg = JSON.parse(data.toString());
console.log(msg));
});

For production use, prefer a full CDP client library. The protocol is message-based and asynchronous — you send commands with incrementing id fields and match responses by id.

The default context

Each session has one browser context with pre-warmed state: cookies, localStorage, and navigation history that a real browser would have. Use it:

const context = browser.contexts()[0];

Do not call browser.createBrowserContext() or browser.newContext(). Additional contexts are not supported and will error.

Pages

The default context may have a blank page open. You can use it or create a new one:

const pages = context.pages();
const page = pages.length > 0 ? pages[0] : await context.newPage();

Timeouts

CDP connections to Chaser sessions are subject to the session’s TTL. If the session expires while you’re connected, the WebSocket closes. Handle the close event and re-create the session if needed:

browser.on("disconnected", () => {
console.log("Session ended — create a new one to continue");
});

What you can do

The full CDP surface is available:

  • Page.navigate, Page.reload, Page.goBack
  • Runtime.evaluate (run JavaScript in the page)
  • DOM.getDocument, DOM.querySelector
  • Input.dispatchKeyEvent, Input.dispatchMouseEvent
  • Network.setCookie, Network.getCookies
  • Emulation.setDeviceMetricsOverride (avoid — it breaks fingerprint consistency)

What to avoid

ActionWhy
Emulation.setDeviceMetricsOverrideOverrides the screen metrics, breaking fingerprint consistency
Emulation.setUserAgentOverrideOverrides the user-agent, breaking client-hint consistency
Network.setBlockedURLsUnnecessary — the session already routes through residential egress
Creating new browser contextsNot supported; use the default context
browser.close()This closes your local connection, not the remote session. Use DELETE /v1/sessions/{id} to end the session.