Get the Puppeteer WebSocket Endpoint
Get a browser’s WebSocket endpoint with Puppeteer, reconnect to a running browser, and fix common connection problems.
Call browser.wsEndpoint() on a Puppeteer Browser instance. It returns the WebSocket URL for that specific browser. To attach Puppeteer to a browser that is already running, pass its actual endpoint to puppeteer.connect({ browserWSEndpoint }). The endpoint generally looks like ws://HOST:PORT/devtools/browser/<id>; host, port, and ID vary by browser instance. Puppeteer API reference: Browser.wsEndpoint().
Get the endpoint from a browser Puppeteer launched
This runnable Node.js example launches a browser, prints its endpoint, disconnects Puppeteer while leaving the browser process running, then reconnects and closes it. Install Puppeteer first with npm install puppeteer, then save this as endpoint.mjs and run node endpoint.mjs.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const browserWSEndpoint = browser.wsEndpoint();
console.log(browserWSEndpoint);
// Detach this Puppeteer client. The browser remains open.
await browser.disconnect();
// Attach again using the endpoint from this same browser instance.
const reconnectedBrowser = await puppeteer.connect({ browserWSEndpoint });
console.log('Connected:', reconnectedBrowser.connected);
// Close the browser process when finished.
await reconnectedBrowser.close();
wsEndpoint() is synchronous and returns a string. It does not start a browser or create an endpoint for a browser that Puppeteer is not connected to. The API reference documents the endpoint shape as ws://HOST:PORT/devtools/browser/<id>.
Connect to an already running browser
If another process, container, or browser service started Chrome, get the WebSocket endpoint from that environment and supply it to connect(). A common discovery endpoint is http://HOST:PORT/json/version; its JSON response can include a webSocketDebuggerUrl. Use the host and port reachable from the machine running your Node process.
import puppeteer from 'puppeteer';
// Replace this value with the endpoint reported by your browser or service.
const browserWSEndpoint = process.env.BROWSER_WS_ENDPOINT;
if (!browserWSEndpoint) {
throw new Error('Set BROWSER_WS_ENDPOINT to the browser WebSocket URL');
}
const browser = await puppeteer.connect({ browserWSEndpoint });
try {
const pages = await browser.pages();
console.log(`Connected; open pages: ${pages.length}`);
} finally {
// Detach Puppeteer without shutting down the remotely managed browser.
await browser.disconnect();
}
Set the environment variable to the real endpoint supplied by your browser runtime; do not copy a sample URL or another machine’s browser ID. For example, if the endpoint returned by your own browser is ws://HOST:PORT/devtools/browser/ID, pass that full string unchanged.
Puppeteer also supports browserURL in its connection options for browser URLs where that is the connection information available. Consult the ConnectOptions reference and your browser provider’s instructions to choose the right option. When a provider gives you a WebSocket URL, use browserWSEndpoint. Puppeteer browser management guide.
Endpoint lookup options and connection lifecycle
| Situation | What to do | Important detail |
|---|---|---|
| Puppeteer launched the browser | Call browser.wsEndpoint() |
Use the value for that browser instance. |
| Another process launched the browser | Read the endpoint from its output or service configuration; optionally inspect http://HOST:PORT/json/version |
Use an address reachable from the Puppeteer process. |
| Connect to a known WebSocket URL | puppeteer.connect({ browserWSEndpoint }) |
connect() resolves to a Puppeteer Browser. |
| Stop controlling but keep browser open | await browser.disconnect() |
Pages and browser remain running. |
| Shut down a browser Puppeteer owns | await browser.close() |
Closes the browser, unlike disconnect. |
The endpoint is a connection address, not a durable identifier. A browser restart or replacement can produce a different endpoint. Retrieve it from the current instance rather than storing a copied example as a permanent value.
Run the endpoint lookup from cURL, Python, or Node.js
browser.wsEndpoint() is a Puppeteer JavaScript API, so the direct lookup requires Node.js and a Puppeteer Browser. cURL and Python can retrieve the debugger metadata endpoint when the browser exposes its HTTP debugging interface. The following examples print the webSocketDebuggerUrl field from /json/version.
cURL
curl --fail --silent --show-error http://HOST:PORT/json/version
Inspect the JSON output for webSocketDebuggerUrl. To extract the field with jq:
curl --fail --silent --show-error http://HOST:PORT/json/version \
| jq -r .webSocketDebuggerUrl
Python
import json
from urllib.request import urlopen
url = 'http://HOST:PORT/json/version'
with urlopen(url, timeout=10) as response:
info = json.load(response)
print(info['webSocketDebuggerUrl'])
Node.js
const response = await fetch('http://HOST:PORT/json/version');
if (!response.ok) {
throw new Error(`Endpoint lookup failed: HTTP ${response.status}`);
}
const info = await response.json();
console.log(info.webSocketDebuggerUrl);
Replace HOST:PORT with the actual browser debugging address. These HTTP lookup examples do not enable remote debugging or expose a browser on their own; the browser must already be running with its debugging interface available. For Puppeteer-launched browsers, calling wsEndpoint() is simpler and avoids an extra HTTP lookup.
Relevant connection options
The endpoint is only one part of a connection. Puppeteer’s ConnectOptions reference lists connection settings; use only those needed by your environment.
browserWSEndpoint: the WebSocket URL of the browser to attach to.browserURL: an alternative browser address option, useful when the available connection information is a browser URL rather than a WebSocket URL.defaultViewport: controls the viewport Puppeteer applies to pages after connecting; its documented default is 800 by 600. Set it tonullif you need to retain the browser’s default viewport.protocolTimeout: maximum time in milliseconds for an individual protocol call; the current reference documents a default of 180,000 ms.slowMo: adds a delay between Puppeteer operations, which can help while debugging.headers/wsOptions: WebSocket connection configuration. The reference marksheadersdeprecated in favor ofwsOptions.headers.protocol: selects the protocol; when connecting, Puppeteer determines this at runtime unless configured.
Options vary by Puppeteer version and runtime. Check the reference matching your installed package before relying on experimental or deprecated options.
Security, reliability, and performance
- Protect the endpoint. Treat a browser debugging endpoint as privileged access to that browser. Avoid putting it in public logs, source control, client-side code, or unauthenticated network interfaces. Use your browser provider’s access controls and network boundaries.
- Respect network boundaries.
localhostmeans the machine or container where the Node process runs. In a container, that may not be the host where Chrome is running. Use the service’s reachable hostname and port. - Refresh after restarts. A restarted browser may have a new WebSocket URL. Fetch the endpoint from the current process or service each time it starts, and reconnect with retry/backoff if the browser is expected to restart.
- Use deliberate ownership. Call
disconnect()when detaching from a shared or remotely managed browser. Callclose()only when this process should shut down the browser it owns. - Reuse appropriately. Reusing a connection avoids repeatedly attaching Puppeteer, but long-lived jobs should handle disconnects and stale endpoints. Endpoint lookup itself is a small local metadata request; actual automation and page loads generally dominate runtime.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
browser.wsEndpoint is not a function |
The value is not a Puppeteer Browser, or code is using a different object/API. |
Confirm the launch or connect call resolved to a Browser instance: const browser = await puppeteer.launch(). |
Connection refused or timeout |
Wrong host or port, browser stopped, debugging endpoint unavailable, or network path blocked. | Verify the browser is running and check http://HOST:PORT/json/version from the same environment as Node. |
| WebSocket handshake or HTTP 404 error | Copied an incomplete endpoint, used the HTTP discovery URL as a WebSocket URL, or endpoint belongs to a different browser. | Copy the complete webSocketDebuggerUrl and pass it as browserWSEndpoint. |
| It connected before, but not after restart | The old endpoint identified the previous browser instance. | Read the new endpoint after each browser start; do not persist an instance URL across restarts. |
| Works locally but not in a container or remote host | The endpoint host resolves from a different network namespace, or the service is not reachable there. | Use the address accessible from the Puppeteer process and verify routing and access policy. |
| Browser unexpectedly remains open | disconnect() detaches the client without closing the browser. |
Use close() if this code owns the browser and should terminate it. |
| Browser unexpectedly shuts down | close() was used when only detaching was intended. |
Use disconnect() for a remote/shared browser that should continue running. |
| HTTP lookup returns no endpoint field | The response is not the browser’s version JSON or the debugging interface differs. | Check the response body and browser provider documentation; use the endpoint that provider exposes. |
Or skip the browser setup
If the goal is a screenshot rather than browser automation, ScreenshotNeo returns an image or PDF from one GET request. Its API accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, and failed loads are never billed, and response headers report the page verdict and billing status. An MCP server gives AI agents screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. See the API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python:
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)
Node.js:
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: HTTP ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
Get 1,000 free screenshots a month with no card.
FAQ
Is the WebSocket endpoint the same as a page URL?
No. It connects a Puppeteer client to the browser’s debugging interface; navigate a page separately after connecting.
Can I generate an endpoint without starting or accessing a browser?
No. The endpoint belongs to a running browser instance. Launch one or obtain the address from the service managing it.
Does disconnecting invalidate the endpoint?
Disconnecting detaches that Puppeteer client. The browser remains open, so the endpoint can be used to reconnect while that browser instance is still running.
Does Puppeteer return a WebSocket endpoint for every browser?
wsEndpoint() is a method on Puppeteer’s Browser object. For browsers started elsewhere, use the endpoint that browser or its service exposes.


