ScreenshotNeo

BlogHow-to

Get the Puppeteer WebSocket Endpoint

Get a browser’s WebSocket endpoint with Puppeteer, reconnect to a running browser, and fix common connection problems.

By the ScreenshotNeo team4 October 20267 min read

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 to null if 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 marks headers deprecated in favor of wsOptions.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. localhost means 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. Call close() 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.