Disconnect and Reconnect to a Browser
Keep a browser session alive for a short window after disconnecting and reconnect to it with all cookies, localStorage, and state intact. Ideal for scripts that need to briefly detach and resume within seconds or minutes.
Need cookies and data to survive for hours or days between runs? See Continue Browser State Across Runs for an approach that keeps browser state alive much longer.
- A Browserless API token from your account dashboard
Steps
Reconnection works in two phases: first you start a session and request a reconnection endpoint, then you reconnect using the returned URL. The browser stays alive in the background for the duration you specify.
- REST API
- Frameworks
- BQL
Use BrowserQL over HTTP to start a session, perform actions, and get a reconnection endpoint for follow-up requests.
- cURL
- JavaScript
- Python
- Java
- C#
1. Start a session and get the reconnection endpoint
Send a BQL mutation that navigates to a page and calls reconnect to keep the browser alive:
curl -X POST \
"https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation StartSession { goto(url: \"https://example.com\", waitUntil: domContentLoaded) { status } reconnect(timeout: 60000) { browserQLEndpoint browserWSEndpoint } }",
"variables": {},
"operationName": "StartSession"
}'
2. Reconnect with the returned endpoint
Use the browserQLEndpoint from the response (append your token) to send a follow-up mutation to the same browser:
curl -X POST \
"RETURNED_BROWSERQL_ENDPOINT?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation ContinueSession { html { html } }",
"variables": {},
"operationName": "ContinueSession"
}'
3. Check the output
The first response includes the reconnection URLs:
{
"data": {
"goto": { "status": 200 },
"reconnect": {
"browserQLEndpoint": "https://production-sfo.browserless.io/stealth/bql/abc123",
"browserWSEndpoint": "wss://production-sfo.browserless.io/chromium/abc123"
}
}
}
The second response returns the page HTML from the still-running browser.
1. Start a session and reconnect
const TOKEN = 'YOUR_API_TOKEN_HERE';
const BQL_URL = `https://production-sfo.browserless.io/stealth/bql?token=${TOKEN}`;
const startQuery = `mutation StartSession {
goto(url: "https://example.com", waitUntil: domContentLoaded) {
status
}
reconnect(timeout: 60000) {
browserQLEndpoint
}
}`;
const startResponse = await fetch(BQL_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: startQuery,
variables: {},
operationName: 'StartSession',
}),
});
const { data } = await startResponse.json();
const reconnectURL = `${data.reconnect.browserQLEndpoint}?token=${TOKEN}`;
const continueResponse = await fetch(reconnectURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: 'mutation ContinueSession { html { html } }',
variables: {},
operationName: 'ContinueSession',
}),
});
const result = await continueResponse.json();
console.log(result.data.html.html.substring(0, 200));
2. Check the output
Run with node reconnect.mjs. The script navigates, disconnects, reconnects, and prints the page HTML from the still-running session.
1. Install dependencies
pip install requests
2. Start a session and reconnect
import requests
TOKEN = 'YOUR_API_TOKEN_HERE'
BQL_URL = f'https://production-sfo.browserless.io/stealth/bql?token={TOKEN}'
start_query = """
mutation StartSession {
goto(url: "https://example.com", waitUntil: domContentLoaded) {
status
}
reconnect(timeout: 60000) {
browserQLEndpoint
}
}
"""
start_response = requests.post(
BQL_URL,
json={
'query': start_query,
'variables': {},
'operationName': 'StartSession',
},
)
data = start_response.json()['data']
reconnect_url = f"{data['reconnect']['browserQLEndpoint']}?token={TOKEN}"
continue_response = requests.post(
reconnect_url,
json={
'query': 'mutation ContinueSession { html { html } }',
'variables': {},
'operationName': 'ContinueSession',
},
)
result = continue_response.json()
print(result['data']['html']['html'][:200])
3. Check the output
Run with python reconnect.py. The script prints the page HTML from the reconnected session.
1. Start a session and reconnect
import java.net.URI;
import java.net.http.*;
String token = "YOUR_API_TOKEN_HERE";
String bqlUrl = "https://production-sfo.browserless.io/stealth/bql?token=" + token;
String startPayload = """
{
"query": "mutation StartSession { goto(url: \\"https://example.com\\", waitUntil: domContentLoaded) { status } reconnect(timeout: 60000) { browserQLEndpoint } }",
"variables": {},
"operationName": "StartSession"
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest startRequest = HttpRequest.newBuilder()
.uri(URI.create(bqlUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(startPayload))
.build();
HttpResponse<String> startResponse = client.send(startRequest, HttpResponse.BodyHandlers.ofString());
String body = startResponse.body();
// Extract browserQLEndpoint from the JSON response
String marker = "\"browserQLEndpoint\":\"";
int start = body.indexOf(marker) + marker.length();
int end = body.indexOf("\"", start);
String reconnectUrl = body.substring(start, end) + "?token=" + token;
// Send follow-up mutation to the reconnected session
String continuePayload = """
{
"query": "mutation ContinueSession { html { html } }",
"variables": {},
"operationName": "ContinueSession"
}
""";
HttpRequest continueRequest = HttpRequest.newBuilder()
.uri(URI.create(reconnectUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(continuePayload))
.build();
HttpResponse<String> continueResponse = client.send(continueRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(continueResponse.body());
2. Check the output
Run the class. The script starts a session, reconnects, and prints the page HTML from the still-running browser.
1. Start a session and reconnect
using System.Net.Http;
using System.Text;
using System.Text.Json;
string token = "YOUR_API_TOKEN_HERE";
string bqlUrl = $"https://production-sfo.browserless.io/stealth/bql?token={token}";
var startPayload = new
{
query = @"mutation StartSession {
goto(url: ""https://example.com"", waitUntil: domContentLoaded) { status }
reconnect(timeout: 60000) { browserQLEndpoint }
}",
variables = new { },
operationName = "StartSession",
};
using (HttpClient httpClient = new HttpClient())
{
var json = JsonSerializer.Serialize(startPayload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var startResponse = await httpClient.PostAsync(bqlUrl, content);
string startBody = await startResponse.Content.ReadAsStringAsync();
var doc = JsonDocument.Parse(startBody);
string reconnectUrl = doc.RootElement
.GetProperty("data")
.GetProperty("reconnect")
.GetProperty("browserQLEndpoint")
.GetString() + $"?token={token}";
var continuePayload = new
{
query = "mutation ContinueSession { html { html } }",
variables = new { },
operationName = "ContinueSession",
};
var continueContent = new StringContent(
JsonSerializer.Serialize(continuePayload), Encoding.UTF8, "application/json");
var continueResponse = await httpClient.PostAsync(reconnectUrl, continueContent);
string continueBody = await continueResponse.Content.ReadAsStringAsync();
Console.WriteLine(continueBody);
}
2. Check the output
Run the program. The script reconnects to the still-running browser and prints the page HTML.
Use the Browserless.reconnect CDP command to keep a session alive and reconnect with Puppeteer or Playwright.
- Puppeteer
- Playwright
1. Install dependencies
npm install puppeteer-core
2. Start a session and request reconnection
import puppeteer from 'puppeteer-core';
const TOKEN = 'YOUR_API_TOKEN_HERE';
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io?token=${TOKEN}`,
});
try {
const page = await browser.newPage();
const cdp = await page.createCDPSession();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
// Keep the browser alive for 60 seconds after disconnecting.
const { browserWSEndpoint } = await cdp.send('Browserless.reconnect', {
timeout: 60000,
});
// Disconnect (does not kill the browser — it stays alive).
await browser.close();
console.log('Reconnect URL:', browserWSEndpoint);
// Reconnect to the same browser session.
const reconnected = await puppeteer.connect({
browserWSEndpoint: `${browserWSEndpoint}?token=${TOKEN}`,
});
const pages = await reconnected.pages();
const existingPage = pages[0];
console.log('Still on:', await existingPage.title());
await reconnected.close();
} catch (err) {
await browser.close();
throw err;
}
3. Check the output
Run with node reconnect.mjs. The script connects, navigates, disconnects, reconnects, and confirms the page title is preserved.
1. Install dependencies
npm install playwright-core
2. Start a session and request reconnection
import { chromium } from 'playwright-core';
const TOKEN = 'YOUR_API_TOKEN_HERE';
const browser = await chromium.connect(
`wss://production-sfo.browserless.io/chromium/playwright?token=${TOKEN}`
);
try {
const page = await browser.newPage();
const cdp = await page.context().newCDPSession(page);
await page.goto('https://example.com', { waitUntil: 'networkidle' });
// Keep the browser alive for 60 seconds after disconnecting.
const { browserWSEndpoint } = await cdp.send('Browserless.reconnect', {
timeout: 60000,
});
// Disconnect (does not kill the browser — it stays alive).
await browser.close();
console.log('Reconnect URL:', browserWSEndpoint);
// Reconnect to the same browser session.
const reconnected = await chromium.connect(
`${browserWSEndpoint}?token=${TOKEN}`
);
const pages = reconnected.contexts()[0]?.pages() ?? [];
if (pages.length > 0) {
console.log('Still on:', await pages[0].title());
}
await reconnected.close();
} catch (err) {
await browser.close();
throw err;
}
3. Check the output
Run with node reconnect.mjs. The script connects, navigates, disconnects, reconnects, and confirms the page title is preserved.
1. Write the first mutation
Navigate to a page and call reconnect to keep the browser alive. The response includes a browserQLEndpoint for follow-up requests and a browserWSEndpoint for framework connections:
reconnect(timeout: 60000)requires at least a Starter plan. The Free plan caps reconnect timeouts at 10 seconds. Scale plans support up to 5 minutes.
mutation StartSession {
goto(url: "https://example.com", waitUntil: domContentLoaded) {
status
}
reconnect(timeout: 60000) {
browserQLEndpoint
browserWSEndpoint
}
}
2. Send a follow-up mutation to the same browser
Use the browserQLEndpoint from the response (append ?token=YOUR_API_TOKEN_HERE) and POST a new mutation to it:
mutation ContinueSession {
html {
html
}
}
3. Run it
Paste the first mutation into the BQL IDE and click Run. Copy the browserQLEndpoint from the response, open a new BQL IDE tab pointed at that endpoint, and run the second mutation.
4. Check the output
The first response includes the reconnection URLs:
{
"data": {
"goto": { "status": 200 },
"reconnect": {
"browserQLEndpoint": "https://production-sfo.browserless.io/stealth/bql/abc123",
"browserWSEndpoint": "wss://production-sfo.browserless.io/chromium/abc123"
}
}
}
The second response returns data from the same browser. All cookies, localStorage, and page state are preserved.
Next steps
- Log In and Reuse Sessions — save login state to a named profile for reuse across sessions
- Log In via Email OTP — use reconnect to bridge multi-step login flows
- Save Logins to Authenticated Profiles — capture and replay authenticated browser state