How to Reconnect to a Browser Session with an API
Reconnect to a live browser through its provider-issued endpoint, or preserve state across longer gaps with a session API. Learn the Puppeteer and Playwright tradeoffs.
How to Reconnect to a Browser Session with an API: reconnect using the browser host’s provider-issued WebSocket or CDP endpoint, the required authentication, and the same live session before its timeout. After attaching, inspect the browser’s contexts and pages to find the tab you need. If the browser process may stop or the gap is longer, use a provider’s session API that stores state across runs; an expired live-browser endpoint cannot revive the old process.
There is no universal reconnect URL. The endpoint, protocol, credentials, and session lifetime are specific to the browser host. This guide uses Browserless as a documented example. Confirm current endpoint syntax, package compatibility, authentication, timeout caps, and plan limits in your provider’s documentation before relying on them.
Choose the right session lifetime
First decide whether you need to keep a running browser alive briefly or preserve browser state for a longer gap.
| Need | Use | What to expect |
|---|---|---|
| Brief interruption while the remote browser is still running | Provider’s reconnect feature | Request a reconnect endpoint before disconnecting, then reattach within its finite timeout. Browserless describes a short window of seconds to minutes and documents a built-in limit up to five minutes; verify the current account limit. |
| Longer gap, state across browser restarts, or Playwright workflow on Browserless | Provider’s persistent session API | Create a session with a configured TTL, reconnect through its returned connection URL, and stop/delete it when finished. Retention is bounded by the configured TTL and provider policy. |
A reconnect endpoint points to a particular provider-managed browser and protocol. A BrowserQL endpoint for further BrowserQL queries is not interchangeable with a WebSocket endpoint for Puppeteer or Playwright.
Reconnect to a live browser with Puppeteer
For a short interruption, the sequence is: request the reconnect endpoint while connected, detach without closing the remote browser, then connect again before the endpoint expires. Browserless documents the CDP extension Browserless.reconnect. Its example uses Puppeteer because browser.disconnect() detaches the client while leaving the remote browser process alive.
- Launch or connect to the remote browser using the provider’s current instructions and authentication.
- Ask the provider extension for a reconnect endpoint and save it securely. Do this before the current connection ends.
- Call
browser.disconnect(), notbrowser.close(), when you want the remote process to remain available. - Connect with
puppeteer.connect({ browserWSEndpoint })before the provider’s timeout. - Enumerate pages and choose the intended tab. Do not assume the first page is the one you need.
import puppeteer from 'puppeteer';
// Use the provider-issued endpoint and authentication format from its docs.
const initialEndpoint = process.env.BROWSER_WS_ENDPOINT;
if (!initialEndpoint) throw new Error('Set BROWSER_WS_ENDPOINT');
const browser = await puppeteer.connect({ browserWSEndpoint: initialEndpoint });
// Browserless documents this CDP extension. The exact command and returned
// endpoint syntax are provider-specific; follow the current provider docs.
const reconnectResult = await browser._connection.send('Browserless.reconnect', {
timeout: 60_000,
});
const reconnectEndpoint = reconnectResult?.browserWSEndpoint;
if (!reconnectEndpoint) throw new Error('Provider did not return a reconnect endpoint');
// Detach this client without shutting down the remote browser.
browser.disconnect();
// Reattach promptly. Supply credentials as required by the provider.
const resumedBrowser = await puppeteer.connect({ browserWSEndpoint: reconnectEndpoint });
const pages = await resumedBrowser.pages();
const page = pages.find(p => p.url().includes('example.com')) ?? pages[0];
if (!page) throw new Error('No page was available in the reconnected browser');
console.log('Resumed at:', page.url());
Authentication: Browserless’s example adds its API token to the follow-up endpoint. Returned endpoints may not include credentials. Use the provider’s documented credential format; avoid printing or logging token-bearing URLs. The _connection access shown above is provider-specific and may be an internal Puppeteer interface, so use the exact supported reconnect snippet from the browser host’s current documentation.
Reconnect with Playwright over CDP
Playwright’s chromium.connectOverCDP(endpoint) attaches to an existing Chromium browser. Inspect the contexts and pages after attachment, then select the intended page. This is a CDP connection, not Playwright’s native protocol connection: it is Chromium-only and Playwright documents it as significantly lower fidelity than its native connection.
import { chromium } from 'playwright';
const endpoint = process.env.BROWSER_WS_ENDPOINT;
if (!endpoint) throw new Error('Set BROWSER_WS_ENDPOINT');
const browser = await chromium.connectOverCDP(endpoint);
const contexts = browser.contexts();
const pages = contexts.flatMap(context => context.pages());
const page = pages.find(candidate => candidate.url().includes('example.com')) ?? pages[0];
if (!page) {
throw new Error('Connected, but no existing page was found');
}
console.log('Resumed at:', page.url());
// Close the local connection according to the provider’s documented lifecycle.
// Do not assume this terminates or preserves the remote process in every service.
await browser.close();
Browserless documents its standard reconnect pattern as requiring Puppeteer’s browser.disconnect(); Playwright does not expose that method, so this pattern is unreliable for Playwright. For Browserless, use its Session API for persistent state with Playwright. If your browser host offers a native Playwright protocol endpoint, prefer that when you need features that are not available over CDP.
Preserve state across longer gaps with a session API
A persistent session API separates the session lifecycle from one client connection. The provider creates a session and returns connection and stop URLs. Set a TTL that covers the expected gap, reconnect through the returned WebSocket endpoint, and explicitly stop the session when it is no longer needed. Browserless’s guide demonstrates this flow and a 300,000 ms TTL as an example; it is a configuration example, not a promise of indefinite retention.
import { chromium } from 'playwright';
const token = process.env.BROWSERLESS_TOKEN;
if (!token) throw new Error('Set BROWSERLESS_TOKEN');
// Use the current Session API URL, request schema, and auth format in the
// provider documentation. This example illustrates the lifecycle only.
const createResponse = await fetch(process.env.SESSION_CREATE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Replace with the provider's documented authentication mechanism.
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ ttl: 300_000 }),
});
if (!createResponse.ok) throw new Error(`Session create failed: ${createResponse.status}`);
const session = await createResponse.json();
if (!session.connect || !session.stop) throw new Error('Expected connect and stop URLs');
try {
let browser = await chromium.connectOverCDP(session.connect);
let pages = browser.contexts().flatMap(context => context.pages());
console.log('Initial pages:', pages.length);
// Disconnect and later reconnect using the same session's connect URL.
await browser.close();
browser = await chromium.connectOverCDP(session.connect);
pages = browser.contexts().flatMap(context => context.pages());
console.log('Reconnected pages:', pages.length);
await browser.close();
} finally {
const stopResponse = await fetch(session.stop, { method: 'DELETE' });
if (!stopResponse.ok) console.error('Session cleanup failed:', stopResponse.status);
}
Important: The session creation endpoint, authentication headers, request body, and stop method must match the provider’s current API. The code uses environment variables for provider-specific URLs so it does not suggest a universal endpoint. Browserless’s documentation returns connect and stop URLs from its Session API and demonstrates reconnecting with Playwright over CDP.
Use cURL to inspect a session API response
cURL is useful for creating or inspecting a provider-managed session when its API supports REST. It does not itself attach a browser automation client to the returned WebSocket endpoint. Use the exact provider URL, authentication method, and body from that provider’s docs.
curl --fail-with-body --silent --show-error \
-X POST "$SESSION_CREATE_URL" \
-H "Authorization: Bearer $BROWSER_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"ttl":300000}'
Store the returned connection and stop URLs as secrets. Do not put tokens or signed endpoints in source control, support tickets, screenshots, or routine request logs.
Security and lifecycle checklist
- Keep the WebSocket endpoint and provider credentials in a secret store or environment variables, not source code.
- Use only the endpoint type expected by the next client: for example, BrowserQL for BQL requests and WebSocket/CDP for a browser library.
- Request a reconnect endpoint before detaching if the provider requires it.
- Use a short timeout that fits the work, within the provider and plan maximum.
- Track session ownership and cleanup. Stop persistent sessions when complete to avoid leaving resources allocated.
- After reconnecting, verify the current URL and page state before continuing an action that changes data.
- Never treat an old endpoint as a way to restore an expired process; create a new browser or session and restore state through an explicit supported mechanism.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Reconnect returns 404 or the WebSocket fails after waiting | The reconnect window expired; the provider shut down the browser. | Reconnect sooner or request an allowed timeout that covers the expected pause. If already expired, start a new session and restore state through a persistent session feature. |
| 401 Unauthorized on the follow-up connection | The returned endpoint did not include the token, or credentials were omitted or placed in the wrong location. | Supply authentication exactly as the current provider docs specify. Keep credentials separate from logs and error reports. |
| The browser closes despite requesting an idle timeout | The provider’s maximum session duration or account plan limit was reached. | Check the current maximum duration for the account and plan; an idle timeout cannot extend a hard duration cap. |
| Connection succeeds, but the expected page is missing | The browser has multiple contexts or tabs, or the wrong session endpoint was used. | Enumerate contexts and pages, inspect URLs, and select the expected tab. Confirm the endpoint belongs to the original session. |
| BrowserQL query endpoint rejects Puppeteer or Playwright | A query endpoint was used where a WebSocket/CDP endpoint is required. | Use the provider’s WebSocket endpoint for the automation library. Use its BrowserQL endpoint only for BrowserQL requests. |
| Advanced Playwright behavior fails after connecting | connectOverCDP has lower fidelity and works only with Chromium. |
Use a supported native Playwright protocol connection if the browser host offers one, or constrain the workflow to supported Chromium/CDP behavior. |
| Playwright disconnect shuts down or destabilizes a Browserless standard session | Browserless’s documented standard reconnect flow depends on Puppeteer’s browser.disconnect(), which Playwright does not expose. |
Use Browserless’s persistent Session API with Playwright and follow its documented lifecycle. |
| Session resource remains after the job ends | The workflow disconnected but did not call the returned stop/delete operation. | Put cleanup in a finally block and monitor failed cleanup responses for retry. |
Performance, reliability, and cost
Latency and responsiveness
Reusing a live browser can avoid launching and initializing another browser, but actual time savings depend on the provider, browser workload, and network. The session must remain within its timeout, and the client must retain the correct endpoint. Measure the end-to-end workflow that matters to you rather than assuming reconnect is faster in every case.
Reliability
Treat a live reconnect window as a lease: it can expire, and provider plan limits can end the browser even when the client expects it to remain available. For critical workflows, record a stable application-level checkpoint as well as the browser endpoint. A persistent session can preserve provider-managed state across runs, but it still has a configured TTL and provider limits. Make recovery idempotent where possible so a retry does not submit a form or purchase twice.
Cost and cleanup
Pricing and resource accounting depend on the browser host and plan; the researched provider documentation does not establish a universal cost per session. Check how your provider counts browser time, storage, concurrent sessions, and failed attempts. Stop sessions promptly, set the smallest TTL that fits the job, and avoid keeping an idle browser alive as a substitute for durable application data.
Or skip the browser setup
If your goal is a screenshot rather than continuing automation in the same live browser, [ScreenshotNeo](https://screenshotneo.com) takes a URL in one API request and returns an image or PDF. Its API does not reconnect to an existing browser session; it handles the capture for you. See the ScreenshotNeo API documentation for parameters and options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.
FAQ
Can I reconnect after the browser has shut down?
No. A live-browser reconnect endpoint expires with the session. Start a new session and restore state through a provider-supported persistent session or your own saved application state.
Does a WebSocket endpoint work with every browser library?
No. The host, protocol, browser engine, and library must be compatible. Playwright’s CDP attachment is for Chromium; check whether the provider offers the protocol your chosen library expects.
Does reconnecting preserve cookies and local storage?
A reconnect to the same live browser preserves its in-memory browser state. Longer-lived session APIs may preserve state across restarts according to their documented storage and TTL behavior. Verify what the provider persists.
Can ScreenshotNeo reconnect to my existing browser?
No. ScreenshotNeo captures a URL through its screenshot API; it is useful when you need an image or PDF without managing a browser session.


