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

CDP Extensions

Browserless-specific Chrome DevTools Protocol methods you call via cdp.send() on a page's CDP session in Puppeteer or Playwright. They extend standard CDP with live URLs, captcha solving, session reconnect, recording, file transfer, and page identification. Several also emit CDP events (listen with cdp.on()).

Prerequisites
  • A Browserless API token from your account dashboard
  • An existing WebSocket connection to Browserless via Puppeteer or Playwright
  • Familiarity with creating a CDP session (page.createCDPSession() in Puppeteer, page.context().newCDPSession(page) in Playwright)

Captcha solving, reconnect, and recording are each gated on their own feature flag / query param and return an error in the result when unavailable.

Usage​

const browser = await puppeteer.connect({ browserWSEndpoint: '...' });
const [page] = await browser.pages();
const cdp = await page.createCDPSession();

// Command: get a live URL for debugging
const { liveURL } = await cdp.send('Browserless.liveURL', { quality: 70 });

// Event: react to an auto-detected captcha
cdp.on('Browserless.captchaFound', ({ type, status }) => {});

Commands​

Browserless.liveURL​

Mints a temporary URL that opens a live, interactive view of the current page in any browser. For debugging, QA, or human-in-the-loop workflows. The Live URL timeout sets the URL's validity and post-disconnect keep-alive window. The browser and viewer still end at the existing session deadline; minting a Live URL never resets or extends that deadline. Each Live URL accepts up to five concurrent viewers (a sixth connection receives HTTP 429). It binds to the page whose CDP session sends it. For one link per tab, see Multi-Tab Workflows.

Call via CDP: cdp.send('Browserless.liveURL', { quality: 70 })

Returns: { error, liveURLId, liveURL } (error is null on success).

ParameterTypeDefaultDescription
qualitynumber70Stream image quality (1-100).
typestring"jpeg"Stream frame image format.
timeoutnumber30000How long (ms) the minted URL stays valid.
interactablebooleantrueAllow the viewer to click/type into the page.
resizablebooleantrueAllow the viewer to resize the viewport.
showBrowserInterfacebooleanfalseShow browser chrome around the page.
compressedbooleantrueCompress the stream.
emulateComponentsbooleantrueEmulate UI components in the stream.

Browserless.closeLiveURL​

Closes a live URL opened with liveURL, freeing server resources. Called automatically when the session ends.

Call via CDP: cdp.send('Browserless.closeLiveURL', { liveURLId })

Returns: { error, liveURLId }.

ParameterTypeDescription
liveURLIdstringRequired. The liveURLId returned by liveURL.

Browserless.solveCaptcha​

Detects a CAPTCHA on the current page and attempts to solve it via a third-party solving service (reCAPTCHA v2/v3, Cloudflare Turnstile, and more). Requires captcha solving to be available for the session.

Call via CDP: cdp.send('Browserless.solveCaptcha')

Returns: { ok, captchaFound, solveAttempted, solved, token?, message?, error? }.

Browserless.reconnect​

Returns a WebSocket URL for reconnecting to this browser session after disconnecting. The browser stays alive server-side for timeout ms. Requires reconnect to be enabled; on hosted Cloud plans a timeout above the plan's maximum session duration returns an error instead of being silently clamped. The browser still terminates at its original session deadline even if the TTL has time left.

Call via CDP: cdp.send('Browserless.reconnect', { timeout: 60000 })

Returns: { auth, error, browserWSEndpoint } (browserWSEndpoint is null on error).

ParameterTypeDescription
timeoutnumberHow long (ms) to keep the browser alive after disconnect. Defaults to the deployment's configured connection timeout.
authstringOptional auth token echoed back and required on the reconnect handshake.

Browserless.pageId​

Returns Browserless's stable identifier for the current page target, useful for routing and correlating a page across reconnects.

Call via CDP: cdp.send('Browserless.pageId')

Returns: { pageId: '...' }.

Browserless.startRecording​

Starts server-side video recording of the page. Requires connecting with ?record=true. Refused after a credential has been filled on the session (the recording could capture the secret).

Call via CDP: cdp.send('Browserless.startRecording', { width, height })

Returns: { recording, error? }.

ParameterTypeDescription
widthnumberOptional recording width.
heightnumberOptional recording height.

Browserless.stopRecording​

Stops the recording started with startRecording and returns the video. Requires ?record=true.

Call via CDP: cdp.send('Browserless.stopRecording', { encoding: 'base64' })

Returns the encoded recording, or { error }.

ParameterTypeDescription
encodingstring"binary" or "base64".

Browserless.stopSessionRecording​

Stops rrweb session-replay capture, then processes and uploads the events. Requires connecting with ?replay=true.

Call via CDP: cdp.send('Browserless.stopSessionRecording')

Returns: { error } (null on success).

Browserless.uploadFile​

Uploads one or more files into a file <input> on the page, matched by selector. Total decoded size is capped per request.

Call via CDP: cdp.send('Browserless.uploadFile', { selector, files })

Returns: { ok, error? }.

ParameterTypeDescription
selectorstringRequired. CSS selector for the file input.
filesarrayRequired. Objects of { name, content, mimeType? } where content is base64 and mimeType is inferred from name if omitted.

Browserless.setDownloadEnabled​

Toggles streaming of completed downloads back to the client as Browserless.fileDownloaded events. Off by default, so download bytes never flow unless you opt in.

Call via CDP: cdp.send('Browserless.setDownloadEnabled', { enabled: true })

Returns: { ok, enabled }.

ParameterTypeDescription
enabledbooleanWhether to emit fileDownloaded events.

Events​

Subscribe with cdp.on('<EventName>', handler).

Browserless.heartbeat​

Periodic keep-alive emitted on each attached session at the server's heartbeat interval. No params.

Browserless.captchaFound​

Emitted when a CAPTCHA is auto-detected on a page.

Params: { type, status } — status is "solving" when auto-solve is on, otherwise "found".

Browserless.captchaAutoSolved​

Emitted after an auto-solve attempt completes (only when auto-solving is enabled).

Params: the solve result { found, solved, token, time, error? }.

Browserless.fileDownloaded​

Emitted when a download completes, if enabled via setDownloadEnabled. Mirrored to every attached session.

Params: { filename, mimeType, size, data } (data base64), or { filename, mimeType, size, error: 'FileTooLarge', maxBytes } when the file exceeds the transfer limit.

Browserless.liveComplete​

Emitted when an opened Live URL viewer disconnects, its timeout expires, Browserless.closeLiveURL closes it, or credential protection tears the stream down. A URL that is never opened doesn't emit this event, so application code waiting for it should also enforce its own timeout. No params.

Browserless.liveViewerConnected​

Emitted each time a viewer WebSocket attaches to a live URL, including additional viewers and reconnects. Both /live/* and /chromium/live/* viewer routes emit this event.

Params: { liveURLId: string }, identifying the live URL returned by Browserless.liveURL. The event is delivered to attached CDP sessions for that URL's page, not other pages. It signals attachment, not that the viewer has finished interacting or received a frame.

Register the listener before sharing the URL; earlier attachments are not replayed:

const cdp = await page.createCDPSession();
cdp.on('Browserless.liveViewerConnected', ({ liveURLId }) => {
console.log('Viewer connected:', liveURLId);
});
const { liveURL } = await cdp.send('Browserless.liveURL');
// Share liveURL with the viewer.

FAQ & Troubleshooting​

Why does liveURL or reconnect return an error instead of a URL?

The commands have different requirements. For liveURL, an error means invalid authentication, a runtime failure, or an invalid timeout — a timeout that isn't a whole number of milliseconds above 0, or that exceeds your plan's maximum session duration, is rejected when the URL is minted. reconnect has its own reconnect ceiling, and recording requires ?record=true and a plan that includes recording. When a command is unavailable, it returns error instead of the expected field (liveURL, browserWSEndpoint, and so on).

Why isn't my cdp.on() handler firing?

CDP extension events are scoped to the session that triggered them, so cdp.on() has to be attached to the same CDPSession you called cdp.send() on, before the triggering action happens. A handler attached after the event already fired, or attached to a different page's CDP session, never receives it.

Why does Browserless.startRecording fail after I've already filled in a login form?

Recording is refused once a credential has been filled on the session, since the recording could capture the secret. Start recording before any authentication step, or use a separate session for the recorded portion of the flow.

Next steps​

Was this page helpful?