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

Reconnect using Puppeteer & Playwright

The reconnect mutation returns a WebSocket endpoint you can pass to Puppeteer or Playwright. This lets you use BQL for unblocking (solving CAPTCHAs, bypassing bot detection) and then hand off control to your library for everything else.

Prerequisites
Reconnecting with More BQL

You can also reconnect to run more BQL queries instead of connecting a library. See the Reconnect to Browserless guide.

The Reconnect Mutation

Run your BQL mutations (navigation, unblocking, etc.) and call reconnect to get a browserWSEndpoint. This is the WebSocket URL you pass to Puppeteer's connect() or Playwright's connectOverCDP().

The timeout argument controls how long (in milliseconds) the browser waits for a library connection. If nothing connects within this window, the browser shuts down and returns a 404.

mutation Reconnect {
goto(url: "https://www.browserless.io/practice-form/", waitUntil: networkIdle) {
status
}

# Uncomment to solve Cloudflare before handing off:
# solve(type: cloudflare) {
# found
# solved
# time
# }

reconnect(timeout: 30000) {
browserWSEndpoint
}
}

Connecting with Libraries

The browserWSEndpoint from the response connects to any library that supports the Chrome DevTools Protocol (CDP). This includes Puppeteer (puppeteer.connect()) and Playwright (chromium.connectOverCDP()).

Execute your BQL query first, extract the browserWSEndpoint, then pass it to your library. The examples below navigate to a page with BQL, reconnect, and take a screenshot.

Reconnect Using Puppeteer

import puppeteer from 'puppeteer-core';

const url = 'https://www.browserless.io/practice-form';
const token = 'YOUR_API_TOKEN_HERE';
const timeout = 5 * 60 * 1000;

const queryParams = new URLSearchParams({
timeout,
token,
}).toString();

const query = `
mutation Reconnect($url: String!) {
goto(url: $url, waitUntil: networkIdle) {
status
}
reconnect(timeout: 30000) {
browserWSEndpoint
}
}
`;

const variables = { url };

const endpoint = `https://production-sfo.browserless.io/chromium/bql?${queryParams}`;

const options = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
query,
variables,
}),
};

try {
console.log(`Running BQL Query: ${url}`);

const response = await fetch(endpoint, options);

if (!response.ok) {
throw new Error(`Got non-ok response:\n` + (await response.text()));
}

const { data } = await response.json();
const browserWSEndpoint = data.reconnect.browserWSEndpoint
console.log(`Got OK response! Connecting puppeteer to ${browserWSEndpoint}`);
const browser = await puppeteer.connect({
browserWSEndpoint,
});
console.log(`Connected to ${await browser.version()}`);
const pages = await browser.pages();
const page = pages.find((p) => p.url().includes(url));
await page.screenshot({ fullPage: true, path: 'temp.png' });
await browser.close();
} catch (error) {
console.error(error);
}

Reconnect Using Playwright

import playwright from 'playwright-core';

const url = 'https://www.browserless.io/practice-form';
const token = 'YOUR_API_TOKEN_HERE';
const timeout = 5 * 60 * 1000;

const queryParams = new URLSearchParams({
timeout,
token,
}).toString();

const query = `
mutation Reconnect($url: String!) {
goto(url: $url, waitUntil: networkIdle) {
status
}
reconnect(timeout: 30000) {
browserWSEndpoint
}
}
`;

const variables = { url };

const endpoint =
`https://production-sfo.browserless.io/chromium/bql?${queryParams}`;

const options = {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
};

(async () => {
try {
console.log(`Running BQL Query: ${url}`);

const response = await fetch(endpoint, options);

if (!response.ok) {
throw new Error(`Got non-ok response:\n` + (await response.text()));
}

const { data } = await response.json();
const browserWSEndpoint = data.reconnect.browserWSEndpoint;

console.log(`Got OK response! Connecting Playwright to ${browserWSEndpoint}`);

const browser = await playwright.chromium.connectOverCDP(browserWSEndpoint);
const context = browser.contexts()[0];
const page = context.pages().find((p) => p.url().includes(url));

if (!page) {
throw new Error(`Could not find a page matching ${url}`);
}

await page.screenshot({ fullPage: true, path: 'temp.png' });

await browser.close();
} catch (error) {
console.error(error);
}
})();

FAQ & Troubleshooting

Puppeteer/Playwright fails to connect with a 404 error

The reconnect timeout expired before your library connected. Increase the timeout value in the reconnect mutation, or reduce the delay between your BQL query and the library connection.

My page state is lost after handoff

State (cookies, localStorage, navigation history) persists through the handoff. If you're seeing a blank page, make sure you're accessing the correct page in the browser context. Use browser.pages() (Puppeteer) or context.pages (Playwright) to find the active page.

Next steps