Browserless Browser WebSocket APIs API

The Browser WebSocket APIs API from Browserless — 23 operation(s) for browser websocket apis.

Documentation

Specifications

SDKs

Schemas & Data

Other Resources

🔗
Signup
https://account.browserless.io/signup
🔗
SDKs
https://github.com/puppeteer/puppeteer
🔗
SDKs
https://github.com/microsoft/playwright
🔗
SDKs
https://github.com/SeleniumHQ/selenium
🔗
JSONLD
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/json-ld/browserless-context.jsonld
🔗
Examples
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/examples/browserless-screenshot-example.json
🔗
Plans
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/plans/browserless-plans-pricing.yml
🔗
FinOps
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/finops/browserless-finops.yml
🔗
Capabilities
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/capabilities/rest-apis.yaml
🔗
Capabilities
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/capabilities/browserql.yaml
🔗
Capabilities
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/capabilities/sessions.yaml
🔗
Capabilities
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/capabilities/profiles.yaml
🔗
Vocabulary
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/vocabulary/browserless-vocabulary.yml
🔗
Rules
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/rules/browserless-rules.yml
🔗
ChangeLog
https://www.browserless.io/changelog
🔗
StatusPage
https://status.browserless.io
🔗
Pricing
https://www.browserless.io/pricing
🔗
Enterprise
https://www.browserless.io/enterprise
🔗
MCP
https://mcp.browserless.io/mcp
🔗
GraphQL
https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/graphql/browserless-graphql.md

OpenAPI Specification

browserless-browser-websocket-apis-api-openapi.yml Raw ↑
openapi: 3.0.0
info:
  title: Browserless Browser REST APIs Browser WebSocket APIs API
  version: 2.49.0
  x-logo:
    altText: browserless logo
    url: ./docs/browserless-logo-inline.svg
  description: "# Browserless.io\n\nThis service extends the Browserless open-source image with many features and enhancements for teams automating at scale. Notable features include:\n\n- A Chrome-devtools-protocol based API for extending and enhancing libraries in a cross-language way.\n- A new hybrid-automation toolkit with live session interactivity.\n- Robust session management: connect, reconnect, kill and limit what a browser can do.\n- Bleeding features like multiplexing numerous clients into a single Chrome process in an isolated way.\n- The ability to upload and run custom extensions.\n- Run multiple tokens, with access controls on each.\n- Multi-browser with all the robust capabilities already in the open-source images.\n\nThere's a lot to cover here so let's get started!\n\n# Software Keys\n\nThe Enterprise image supports time-limited software keys that allow usage for a specific period without requiring any external connections or callbacks. These keys are cryptographically secure and cannot be reverse engineered. When a key expires, the container will exit with a semantic error code.\n\n## Using a Software Key\n\nTo use a software key, set the `KEY` environment variable when running the container:\n\n```bash\ndocker run -e KEY=your-generated-key browserless/enterprise\n```\n\n# Using the Browserless Proxy\n\n> The Residential proxy is only available for Enterprise and Cloud plans.\n\nBrowserless comes with a built-in mechanism to proxy to what's called \"residential\" IPs. These are IP addresses are sourced from real-users running a proxy server on their home networking machines. Residential proxying is especially useful for things like bypassing certain bot blockages and more.\n\nUsing a residential proxy is as straightforward as adding a few parameters to your library or API calls. Here's the required parameters and the values they support:\n\n- `?proxy=residential`: Specifies that you want to use the residential proxy for this request. Data-center coming soon.\n- `?proxyCountry=us`: Specifies a country you wish to use for the request. A two-digit ISO code.\n- `?proxySticky=true`: If you want to use the same IP address for the entirety of the session. Generally recommended for most cases.\n- `?proxyPreset=px_gov01`: Website-specific proxy configuration. Use `px_gov01` for government websites to ensure optimal proxy vendor selection.\n\nSimply append these to your connection call, REST API calls, or any library call:\n\n`wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN&proxy=residential&proxyCountry=us&proxySticky`\n\n`https://production-sfo.browserless.io/chromium/unblock?token=YOUR-API-TOKEN&proxy=residential&proxyCountry=us&proxySticky`\n\nPlease do note that using a proxy will increase the amount of units consumed. Every megabyte of data transferred consumes 6 units.\n\n# The Browserless CDP API\n\nIn order to enhance the experience with open source libraries like Puppeteer, we decided to take a new approach to extending these libraries in a language-agnostic way. We call it the Browserless CDP API. Here's a quick list of what it can do:\n\n- Generate and give back live URLs for hybrid automation.\n- Solve Captchas.\n- Return your page's unique identifier created by Chrome.\n- Way more coming!\n\nSince most libraries come with a way to issue \"raw\" CDP commands, it's an easy way to drop-in custom behaviors without having to write and maintain a library. Plus you can continue to enjoy using the same packages you've already come to know.\n\nGetting started with this API is pretty simple. For instance, if you want to use the live viewer for a particular page, simply do the following:\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint = 'wss://production-sfo.browserless.io/chromium';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n  const { liveURL } = await cdp.send('Browserless.liveURL');\n\n  // liveURL = 'http://localhost:5555/live/?i=98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\nYou can then visit this URL in any browser to interact with the headless Chrome running someplace else.\n\nSee more below for a full list of the available APIs and features.\n\n## Browserless.liveURL\n\n> This API is available on all plans except the Free plan. [Contact us for more information here.](https://www.browserless.io/contact/)\n\nReturns a fully-qualified URL to load into a web-browser. This URL allows for clicking, typing and other interactions with the underlying page. This URL doesn't require an authorization token, so you're free to share it externally with your own users or employees. If security is a concern, you can set a `timeout` parameter to limit the amount of time this URL is valid for. By default no `timeout` is set and the URL is good as long as the underlying browser is open.\n\nProgrammatic control of the session is also available, so you can close the live session once your code has detected a selector, network call, or whatever else. See the below example for programmatic control.\n\n**Basic example**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n  const { liveURL } = await cdp.send('Browserless.liveURL');\n\n  // liveURL = 'https://production-sfo.browserless.io/live/?i=98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\n**Timeout example**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n  const { liveURL } = await cdp.send('Browserless.liveURL', {\n    timeout: 10000, // 10 seconds to connect!\n  });\n\n  // liveURL = 'https://production-sfo.browserless.io/live/?i=98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\n**Maintaining the width and height**\n\nBy default, Browserless will dynamically change the width and height of the browser to match an end-users screen. This isn't always ideal and can be disabled by setting a `resizable` value to `false`. When this is done, only your script can alter the width and height of the browser:\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n\n  // Width and height will always be 1920x1080\n  // and the Live URL will maintain this aspect ratio\n  await page.setViewport({ width: 1920, height: 1080 });\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n  const { liveURL } = await cdp.send('Browserless.liveURL', {\n    resizable: false,\n  });\n\n  // liveURL = 'https://production-sfo.browserless.io/live/?i=98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\n**Setting a Quality and Type**\n\nSetting a \"quality\" and \"type\" effects the streamed quality of the live URL's client-side resolution. By default, Browserless sets these to quality: 70 and type of \"jpeg\". You can experiment different settings to get an ideal resolutions while keep latency slow. The closer to 100 quality is, the potential for higher perceived latency.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n  const { liveURL } = await cdp.send('Browserless.liveURL', {\n    quality: 70, // Can be 1 - 100. Default is 70\n    type: 'jpeg', // Can be 'jpeg' or 'png'. Default is 'jpeg'\n    compressed: true, // Whether to compress each frame of the streamed image data\n  });\n\n  // liveURL = 'https://production-sfo.browserless.io/live/?i=98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\nIt's also helpful to \"wait\" until the user is done doing what's needed. For that reason, Browserless will fire a custom event when the page is closed as well:\n\n**Wait for close**\n\n> Custom CDP Events are not supported in all libraries, including .NET Playwright.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  const { liveURL } = await cdp.send('Browserless.liveURL');\n\n  console.log(liveURL);\n\n  // Wait for the Browserless.liveComplete event when the live page is closed.\n  // Please not that not all libraries support custom CDP events.\n  await new Promise((r) => cdp.on('Browserless.liveComplete', r));\n\n  console.log('Done!');\n\n  await browser.close();\n})();\n```\n\n**Programmatic Control**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  // Having the liveURLId is required in order to close it later\n  const { liveURL, liveURLId } = await cdp.send('Browserless.liveURL');\n\n  await page.waitForSelector('.my-selector');\n\n  // Calling this CDP API with the \"liveURLId\" will close the session, and terminate the client\n  // further usage of the liveURL will fail and no more human-control is possible\n  await cdp.send('Browserless.closeLiveURL', { liveURLId });\n\n  // Continue to process or interact with the browser, then:\n  await browser.close();\n})();\n```\n\nIt's recommended that you double check the page prior to executing further code to make sure the page is where it should be, elements are present, and so forth. This approach makes it easy to solve hard things like second-factor authentication and more in a trivial fashion.\n\n**Read-only LiveURL Sessions**\n\nThe `interactive: false` option allows you to create read-only LiveURL sessions where users can view the browser but cannot interact with it. This is useful for monitoring or demonstration purposes without allowing user input.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  // Create a read-only LiveURL session that users can view but not interact with\n  const { liveURL } = await cdp.send('Browserless.liveURL', {\n    interactive: false,\n  });\n\n  console.log('Read-only LiveURL:', liveURL);\n\n  await browser.close();\n})();\n```\n\n## Browserless.reconnect\n\n> This API is only available for Enterprise plans. [Contact us for more information here.](https://www.browserless.io/contact/)\n\nReconnecting allows for the underlying Chrome or Chromium process to continue to run for a specified amount of time, and subsequent reconnecting back to it. With this approach you can also \"share\" this connection URL to other clients to connect to the same browser process, allowing you to parallelize via a single Browser process.\n\nOnce a reconnection URL is retrieved, Browserless will intercept close-based commands and stop them from terminating the browser process itself. This prevents clients from accidentally closing the process via `browser.close` or similar.\n\nIn order to use this API, simply call `Browserless.reconnect` as a CDP command. You can, optionally, set a `timeout` or an `auth` property. See the below examples for details\n\n**Basic example with timeout**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  // Allow this browser to run for 10 seconds, then shut down if nothing connects to it.\n  // Defaults to the overall timeout set on the instance, which is 5 minutes if not specified.\n  const { error, browserWSEndpoint } = await cdp.send('Browserless.reconnect', {\n    timeout: 10000,\n  });\n\n  if (error) throw error;\n\n  await browser.close();\n\n  // browserWSEndpoint = 'https://production-sfo.browserless.io/reconnect/98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\nIf you want to enforce authentication, you can pass in an optional `auth` property that clients will need to use in order to connect with. Similar to how authentication works in general, a `token` query-string parameter will need to be applied.\n\n**Authentication example**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint =\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  // Set a custom authentication token that clients have to use in order to connect, or otherwise\n  // receive a 401 Response.\n  const { error, browserWSEndpoint } = await cdp.send('Browserless.reconnect', {\n    auth: 'secret-auth-token',\n  });\n\n  if (error) throw error;\n\n  await browser.close();\n\n  // NOTE the URL here doesn't include the auth token!\n  // browserWSEndpoint = 'https://production-sfo.browserless.io/reconnect/98e83bbfd396241a6963425b1feeba2f';\n})();\n```\n\n**Recursive Example**\n\n```js\nimport puppeteer from 'puppeteer-core';\n\nconst job = async (reconnectURL) => {\n  const browserWSEndpoint =\n    reconnectURL ??\n    'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const [page] = await browser.page();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://example.com');\n\n  // Anytime Browserless.reconnect is called, this restarts the timer back to the provided value,\n  // effectively \"bumping\" the timer forward.\n  const res = await cdp.send('Browserless.reconnect', {\n    timeout: 30000,\n  });\n\n  if (res.error) throw error;\n\n  await browser.close();\n\n  // Continuously reconnect back...\n  return job(res.browserWSEndpoint);\n};\n\njob().catch((e) => console.error(e));\n```\n\n## Browserless.solveCaptcha\n\n> This API is only available for Enterprise and Scale and above plans on Cloud. [Contact us for more information here.](https://www.browserless.io/contact/). Only the `/chrome` and `/chromium` routes support Captcha solving.\n\nBrowserless comes with built-in captcha solving capabilities. We use a variety of techniques to try and mitigate the chances of captchas coming up, but if you happen to run into one you can simply call on our API to solve it.\n\nGiven the amount of possibilities during a captcha solve, the API returns many properties and information in order to help your script be more informed as to what happened. See the below code example for all details and fields returned by the API.\n\nPlease be aware that solving a captcha can take a few seconds up to several minutes, so you'll want to increase your timeouts accordingly for your scripts. Captcha's solved, or attempted to solve, cost 10 units.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browser = await puppeteer.connect({\n    browserWSEndpoint:\n      'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN&timeout=300000',\n  });\n\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  await page.goto('https://www.google.com/recaptcha/api2/demo', {\n    waitUntil: 'networkidle0',\n  });\n\n  const {\n    // A simple boolean indicating whether the script can proceed\n    ok,\n    // Whether or not a captcha was found\n    captchaFound,\n    // A human-readable description of what occurred.\n    message,\n    // Whether a solve was attempted or not\n    solveAttempted,\n    // If the Captcha was solved, only true if found AND solved\n    solved,\n  } = await cdp.send('Browserless.solveCaptcha', {\n    // How long to wait for a Captcha to appear to solve.\n    // Defaults to 10,000ms, or 10 seconds.\n    appearTimeout: 30000,\n  });\n\n  console.log(message);\n\n  if (ok) {\n    await page.click('#recaptcha-demo-submit');\n  } else {\n    console.error(`Error solving captcha!`);\n  }\n\n  await browser.close();\n})().catch((e) => {\n  console.error(e);\n  process.exit(1);\n});\n```\n\nIn general, if an `ok` response is sent back from this API, then your script is good to proceed with further actions. If a captcha is to suddenly appears after an action then you might want to listen for the `Browserless.foundCaptcha` event (see below) and retry solving.\n\n## Browserless.foundCaptcha\n\n> This API is only available for Enterprise and Scale and above plans on Cloud. [Contact us for more information here.](https://www.browserless.io/contact/). Only the `/chrome` and `/chromium` routes support Captcha solving.\n\n> Custom CDP Events are not supported in all libraries, including .NET Playwright.\n\nEmitted whenever a captcha widget is found on the page. Useful for checking if there's a captcha and deciding whether or not to proceed with solving.\n\nThe example below stops until a captcha is found, which may or may not be the case with every website out there.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n// Recaptcha\n(async () => {\n  const browser = await puppeteer.connect({\n    browserWSEndpoint:\n      'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN&timeout=300000',\n  });\n\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n\n  await page.goto('https://www.google.com/recaptcha/api2/demo', {\n    waitUntil: 'networkidle0',\n  });\n\n  // Please note that not all libraries support custom CDP events.\n  await new Promise((resolve) =>\n    cdp.on('Browserless.captchaFound', (params) => {\n      console.log('Found a captcha!');\n      return resolve();\n    }),\n  );\n\n  const { solved, error } = await cdp.send('Browserless.solveCaptcha');\n  console.log({\n    solved,\n    error,\n  });\n\n  // Continue...\n  await page.click('#recaptcha-demo-submit');\n})().catch((e) => {\n  console.error(e);\n  process.exit(1);\n});\n```\n\n### Event Parameters\n\n| Parameter | Type     | Description                                    |\n| --------- | -------- | ---------------------------------------------- |\n| `type`    | `string` | Captcha type (`recaptcha`, `cloudflare`, etc.) |\n| `status`  | `string` | Status: `\"found\"` or `\"solving\"`               |\n\n## Browserless.captchaAutoSolved\n\n> This API is only available for Enterprise and Scale and above plans on Cloud. [Contact us for more information here.](https://www.browserless.io/contact/). Only the `/chrome` and `/chromium` routes support Captcha solving.\n\n> Custom CDP Events are not supported in all libraries, including .NET Playwright.\n\nEmitted whenever a captcha widget is solved in the page by the auto-solving feature. Note that this event is not emitted when a captcha is solved manually.\n\nTo enable this feature, you need to set the `solveCaptchas` option to `true` in your query parameters while connecting to the browser.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n// Helper function to wait for a captcha to be solved\nconst waitForCaptchaResolved = (cdp) => {\n  return new Promise((resolve) => {\n    const onCaptchaFound = (params) => {\n      console.log('Captcha found, type:', params.type, params.status);\n      cdp.on('Browserless.captchaAutoSolved', resolve);\n    };\n\n    cdp.on('Browserless.captchaFound', onCaptchaFound);\n  });\n};\n\n// Recaptcha\n(async () => {\n  const browser = await puppeteer.connect({\n    browserWSEndpoint:\n      'wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN&solveCaptchas=true',\n  });\n  const page = await browser.newPage();\n  const cdp = await page.target().createCDPSession();\n\n  await page.goto('https://www.google.com/recaptcha/api2/demo', {\n    waitUntil: 'networkidle0',\n  });\n  const captchaEvent = await waitForCaptchaResolved(cdp);\n  console.log(captchaEvent);\n\n  await browser.close();\n\n  // Continue...\n  await page.click('#recaptcha-demo-submit');\n})().catch((e) => {\n  console.error(e);\n  process.exit(1);\n});\n```\n\n### Event Parameters\n\n| Parameter    | Type             | Description                                 |\n| ------------ | ---------------- | ------------------------------------------- |\n| `autoSolved` | `true`           | Whether captcha was auto-solved             |\n| `token`      | `null \\| string` | Captcha token if solved                     |\n| `found`      | `boolean`        | Whether a captcha was found                 |\n| `solved`     | `boolean`        | Whether the captcha was successfully solved |\n| `time`       | `number`         | Time taken to solve (in milliseconds)       |\n\n## Browserless.heartbeat\n\n> This API is only available for Enterprise hosted and Starter and above plans on Cloud. [Contact us for more information here.](https://www.browserless.io/contact/).\n\n> Custom CDP Events are not supported in all libraries, including .NET Playwright.\n\nA custom event emitted every several seconds, signaling a live connection. This is useful for a few reasons:\n\n- It ensure that your connection with the browser is still good.\n- Sending data can trigger some load-balancing technologies to not kill the connection.\n\nToday this event is emitted every 30 seconds.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\nconst browserWSEndpoint = `wss://production-sfo.browserless.io/chromium?token=YOUR-API-TOKEN`;\n\n(async () => {\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  await page.goto('https://example.com/');\n  const client = await page.createCDPSession();\n\n  client.on('Browserless.heartbeat', () => {\n    console.log('Browserless.heartbeat');\n  });\n})();\n```\n\n## Browserless.pageId\n\n> This API is only available for Enterprise hosted and Starter and above plans on Cloud. [Contact us for more information here.](https://www.browserless.io/contact/).\n\nA simple helper utility to return the page's unique ID. Since most libraries treat this ID as opaque, and some even hide it, knowing the page's ID can be of great help when interacting with other parts of Browserless.\n\n```js\nimport puppeteer from 'puppeteer-core';\n\n(async () => {\n  const browserWSEndpoint = 'wss://production-sfo.browserless.io/chromium';\n  const browser = await puppeteer.connect({ browserWSEndpoint });\n  const page = await browser.newPage();\n  const cdp = await page.createCDPSession();\n  const { pageId } = await cdp.send('Browserless.pageId');\n\n  // pageId = 'ABC12354AFDC123';\n})();\n```\n\nYou can, optionally, try and \"find\" this ID in puppeteer or similar libraries. Given that puppeteer has this property underscored, it's likely to change or be unavailable in the future, and requires the infamous `// @ts-ignore` comment to allow TypeScript compilation.\n\n```ts\nconst getPageId = (page: Page): string => {\n  // @ts-ignore\n  return page.target()._targetId;\n};\n```\n\n# Changelog\n<blockquote>\n<p>Note that these changes are only for Browserless&#39; cloud, Enterprise and self-hosted Enterprise deployments. For information on the open-source container <a href=\"https://github.com/browserless/browserless/blob/main/CHANGELOG.md\">please refer to this link</a>.</p>\n</blockquote>\n<h1>Latest</h1>\n<ul>\n<li>Dependency updates.</li>\n</ul>\n<h1>v2.95.2</h1>\n<ul>\n<li>Fix in-page WebSocket connections for <code>/function</code> and <code>/download</code> endpoints being blocked on HTTPS deployments due to mixed-content restrictions.</li>\n</ul>\n<h1>v2.95.1</h1>\n<ul>\n<li>Dependency updates.</li>\n<li>Add <code>Browserless.refreshProfile</code> CDP method to update an existing profile&#39;s auth state in place without creating a duplicate.</li>\n<li>Updates to <code>@browserless.io/browserless</code> at <code>v2.49.0</code>.</li>\n<li>Reduce production Docker image size by removing development tooling from the final image.</li>\n</ul>\n<h1>v2.95.0</h1>\n<ul>\n<li>Dependency updates.</li>\n<li>Fix billing and <code>Browser Session Ended</code> tracking for persistent session and reconnect WebSocket connections (<code>/session/connect/:id</code> and <code>/reconnect/:id</code>); each WebSocket connection is now billed independently, restoring usage accounting for persistent-session traffic.</li>\n<li>Add <code>POST /profile/upload</code> and <code>POST /profile/refresh</code> endpoints to create a profile from a pre-captured auth state or replace an existing profile&#39;s state in place, with server-side sanitization of cookies, origins, and IndexedDB plus a diagnostics field describing what was filtered or truncated.</li>\n<li>Fix LiveURL OAuth login for authenticated profiles, and default the browser type to <code>stealth</code> when creating a profile without specifying one.</li>\n</ul>\n<h1>v2.94.1</h1>\n<ul>\n<li>Dependency updates.</li>\n<li>Cap live viewers at 5 per session ID on <code>/live/&lt;id&gt;</code> and return <code>429 Too Many Requests</code> once exceeded, freeing slots automatically when viewers disconnect.</li>\n<li>Include captcha detection and classification in snapshot captures, giving agents better visibility into security challenges encountered during automation.</li>\n<li>Add an optional <code>profile</code> parameter to crawl requests for authenticated scraping; crawls now end early on critical errors, truncate oversized error messages, and return <code>503</code> if the profile service is unavailable.</li>\n</ul>\n<h1>v2.94.0</h1>\n<ul>\n<li>Updates to <code>@browserless.io/browserless</code> at <code>v2.48.3</code>.</li>\n<li>Dependency updates.</li>\n<li>Fix agent snapshot generation when pages contain malformed or empty HTML attributes, so snapshots no longer fail on attribute encoding edge cases.</li>\n<li>Add an optional <code>profile</code> parameter to <code>/scrape</code> requests for server-side hydration of authenticated browser profiles; requests that use a profile now require an API token and return clearer validation errors.</li>\n<li>LiveURL sessions now show distinct end-of-session messages for timeouts versus manual closures, and the message is delivered before the socket disconnects so users actually see it.</li>\n<li>Credit limit error messages now link to <code>https://browserless.io/account/upgrade</code> instead of the generic account page, and have minor grammar fixes.</li>\n</ul>\n<h1>v2.93.0</h1>\n<ul>\n<li>Update <code>@browserless.io/browserless</code> to v2.48.2</li>\n<li>Improve proxy city targeting</li>\n<li>Add tab management methods for agents</li>\n</ul>\n<h1>v2.92.3</h1>\n<ul>\n<li>Add Akamai captcha solving support.</li>\n</ul>\n<h1>v2.92.2</h1>\n<ul>\n<li>Improve browser fingerprint consistency for CPU architecture and font rendering in Docker images.</li>\n<li>Dependency updates</li>\n</ul>\n<h1>v2.92.1</h1>\n<ul>\n<li>Enable agentic browsing for all accounts</li>\n<li>Dependency updates</li>\n</ul>\n<h1>v2.92.0</h1>\n<ul>\n<li>Add authenticated profiles: capture a logged-in browser&#39;s cookies, localStorage, and IndexedDB once via <code>Browserless.saveProfile</code>, then reuse on any session with <code>?profile=&lt;name&gt;</code>.</li>\n<li>Add HTTP API for profile lifecycle: <code>POST /profile</code>, <code>GET /profiles</code>, <code>GET|PUT|DELETE /profile/:name</code>.</li>\n</ul>\n<h1>v2.91.0</h1>\n<ul>\n<li>Add route for agentic browsing</li>\n</ul>\n<h1>v2.90.6</h1>\n<ul>\n<li>Set default <code>/crawl</code> scrape timeout to 150s.</li>\n<li>Add <code>customer.id</code> to OpenTelemetry resource attributes.</li>\n<li>Add <code>Content-Disposition</code> header for PDF API responses.</li>\n<li>Improve hCaptcha handling by hiding the auto-challenge.</li>\n<li>Cache uBOL-home release metadata in the adblock build.</li>\n</ul>\n<h1>v2.90.5</h1>\n<ul>\n<li>Update search limits: Free 1→3, Prototyping 3→5, Starter 5→10, Scale 10→20.</li>\n<li>Dependency updates.</li>\n</ul>\n<h1>v2.90.4</h1>\n<ul>\n<li>Improve DataDome captcha solving reliability.</li>\n</ul>\n<h1>v2.90.3</h1>\n<ul>\n<li>Add captcha solving support for browser-use sessions.</li>\n</ul>\n<h1>v2.90.2</h1>\n<ul>\n<li>Add DataDome captcha solving support.</li>\n<li>Improve smart-scrape captcha detection and post-challenge navigation.</li>\n<li>Add captcha solving support for browser-use sessions.</li>\n<li>Improve <code>/crawl</code> API with enhanced orchestration and documentation.</li>\n<li>Fix timeout override for applicable routes.</li>\n<li>Fix session TTL display for browser sessions.</li>\n<li>Bug fixes and improvements.</li>\n</ul>\n<h1>v2.90.1</h1>\n<ul>\n<li>Bug fixes and improvements.</li>\n</ul>\n<h1>v2.90.0</h1>\n<ul>\n<li>New crawl API for scalable web crawling.</li>\n<li>Fix BQL <code>close()</code> overwriting reconnect <code>keepUntil</code> with zero TTL.</li>\n</ul>\n<h1>v2.89.2</h1>\n<ul>\n<li>Updates to <code>@browserless.io/browserless</code> at <code>v2.46.0</code>.</li>\n<li>Fix 400 errors caused by strict <code>Record&lt;string,string&gt;</code> types in API schemas.</li>\n<li>Fix Docker multi-heavy base image tagging.</li>\n<li>Dependency updates.</li>\n</ul>\n<h1>v2.89.1</h1>\n<ul>\n<li>Updates to <code>@browserless.io/browserless</code> at <code>v2.45.0</code>.</li>\n<li>Fix proxy SSL certificate errors (<code>ERR_CERT_AUTHORITY_INVALID</code>).</li>\n<li>Improve proxy vendor health checks and failover reliability.</li>\n<li>Harden URL validation against SSRF and DNS rebinding.</li>\n<li>Fix session TTL management.</li>\n<li>Fix <code>stopRecording</code> hanging in certain error conditions.</li>\n<li>Fix recording quality for VHS sessions.</li>\n<li>Unify scrape format types across REST API schemas.</li>\n</ul>\n<h1>v2.89.0</h1>\n<ul>\n<li>Add metadata enrichment for <code>/map</code> endpoint.</li>\n<li>Add option to transfer base64 instead of binary for recordings.</li>\n<li>Fix liveURL blank screen.</li>\n<li>Fix bypass CSP for virtual keyboard and emulated <code>&lt;select&gt;</code>s.</li>\n<li>Fix high CPU consumption for <code>/map</code>.</li>\n<li>Add <code>--disable-component-extensions-with-background-pages</code> to stealth args.</li>\n</ul>\n<h1>v2.88.2</h1>\n<ul>\n<li>Updates to <code>browserless.io/browserless</code> at <code>v2.43.0</code>.</li>\n</ul>\n<h1>v2.88.1</h1>\n<ul>\n<li>New <code>/map</code> API.</li>\n<li>Add <code>emulateComponents</code> option for liveURL.</li>\n<li>Dependency updates.</li>\n</ul>\n<h1>v2.87.0</h1>\n<ul>\n<li>Deprecate <code>codes</code> in <code>waitForResponse</code> mutation, favoring <code>statuses</code> instead.</li>\n<li>Fix <code>&lt;select /&gt;</code> elements in liveURL view.</li>\n<li>Enable uploaded extensions cache.</li>\n<li>New <code>/search</code> API.</li>\n</ul>\n<h1>v2.86.1</h1>\n<ul>\n<li>Power-scrape endpoint is now smart-scrape.</li>\n<li>Improve Turnstile captcha detection and solving.</li>\n<li>Improve session replay in reconnect sessions.</li>\n<li>Improve stealth chromium for persistent sessions.</li>\n</ul>\n<h1>v2.86.0</h1>\n<ul>\n<li>Up

# --- truncated at 32 KB (144 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/browserless/refs/heads/main/openapi/browserless-browser-websocket-apis-api-openapi.yml