> For the complete documentation index, see [llms.txt](https://developers.oxylabs.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.oxylabs.io/products/agent-browser.md).

# Agent Browser

A cloud-based headless browser with built-in adaptive security, CAPTCHA management, and residential proxies. Test it with code samples and customize with advanced features.

Agent Browser allows you to run and control remote instances for browser-based automation, testing, and web scraping without managing them locally. It provides built-in adaptive security, automatic CAPTCHA handling, geotargeting, integrated residential proxies, session recording, sticky sessions, and persistent profiles.

## Supported libraries

Agent Browser works with any library that supports the **Chrome DevTools Protocol (CDP)**, including:

* [Playwright](https://playwright.dev/) (Python and Node.js)
* [Puppeteer](https://pptr.dev/) (Node.js)
* AI agent frameworks such as [Stagehand](https://github.com/browserbase/stagehand) and [browser-use](https://github.com/browser-use/browser-use). See [CDP Integration](/products/agent-browser/cdp-integration.md).
* Other CDP-compatible automation frameworks

## Connection details

<table><thead><tr><th width="161">Field</th><th>Description</th></tr></thead><tbody><tr><td><strong>Protocol</strong></td><td><code>wss://</code> (WebSocket Secure)</td></tr><tr><td><strong>Host (Chromium)</strong></td><td><code>hb.oxylabs.io</code></td></tr><tr><td><strong>Authentication</strong></td><td>In the URL, user info – <code>wss://USERNAME:PASSWORD@host</code>. Must include username suffix token (e.g. <code>user_ab12</code>). Building custom <code>Authorization</code> headers is not recommended.</td></tr><tr><td><strong>Transport</strong></td><td>CDP – <code>chromium.connectOverCDP</code> (Playwright) / <code>puppeteer.connect</code> (Puppeteer).</td></tr><tr><td><strong>Domain</strong></td><td><code>oxylabs.io</code> – used for connection endpoints (e.g. <a href="http://hb.oxylabs.io">hb.oxylabs.io</a>), the Dashboard (<a href="http://hb.oxylabs.io/dashboard">hb.oxylabs.io/dashboard</a>), and session inspection (<a href="http://hb.oxylabs.io/novnc">hb.oxylabs.io/novnc</a>).</td></tr><tr><td><strong>Rate limits</strong></td><td><code>100</code> concurrent sessions, <code>10</code> session per second. <a href="#need-a-feature-enabled-1">See more</a>.</td></tr></tbody></table>

## Features

Oxylabs Agent Browser includes built-in, cloud-native features designed to be used through query parameters to your WebSocket connection URL.

<table data-header-hidden><thead><tr><th width="250"></th><th></th></tr></thead><tbody><tr><td><a href="/products/agent-browser/page-handler.md"><strong>Page Handler</strong></a></td><td>Identify every page, handle supported challenges and track page state (default – off).</td></tr><tr><td><a href="/products/agent-browser/captcha-handling.md"><strong>CAPTCHA Handling</strong></a></td><td>Identify pages and handle access challenges automatically (default – off).</td></tr><tr><td><a href="/products/agent-browser/engine-and-fingerprints.md"><strong>Engine &#x26; Fingerprints</strong></a></td><td>Select the browser engine and fingerprint profiles.</td></tr><tr><td><a href="/products/agent-browser/geolocation-and-proxy-selection.md"><strong>Proxy &#x26; Geolocation Targeting</strong></a></td><td>Route sessions through specific countries, states, or cities.</td></tr><tr><td><a href="/products/agent-browser/device-type.md"><strong>Device Emulation</strong></a></td><td>Emulate device-specific fingerprints and viewports.</td></tr><tr><td><a href="/products/agent-browser/session-inspection-and-recording.md#session-inspection-vnc"><strong>Session Inspection (VNC)</strong></a></td><td>Monitor live headless browser sessions.</td></tr><tr><td><a href="/products/agent-browser/session-inspection-and-recording.md#session-recording"><strong>Session Recording</strong></a></td><td>Record browser sessions in video format.</td></tr><tr><td><a href="/products/agent-browser/persistent-sessions-and-profiles.md#persistent-sessions"><strong>Persistent Sessions</strong></a></td><td>Create and manage sticky browser instances.</td></tr><tr><td><a href="/products/agent-browser/persistent-sessions-and-profiles.md#persistent-profiles"><strong>Persistent Profiles</strong></a></td><td>Save and restore cookies/localStorage across sessions.</td></tr></tbody></table>

{% hint style="success" %}
**Using an AI coding assistant?** Set up [Agent Skills](/ai-workflows/agent-skills.md) or the [MCP server](/ai-workflows/mcp.md).
{% endhint %}

### Passing parameters

All features are enabled and configured by appending query parameters directly to your WebSocket Secure endpoint by chaining multiple features together using ampersands (`&`).

```bash
# Example: Connecting to Chrome with US Geolocation and CAPTCHA handling enabled
wss://USER:PASS@hb.oxylabs.io?p_cc=US&solve_captcha=true
```

## Integrations

<table data-header-hidden><thead><tr><th width="154.5"></th><th></th></tr></thead><tbody><tr><td><a href="/products/agent-browser/cdp-integration.md"><strong>CDP Integration</strong></a></td><td>Connect Stagehand and browser-use to open live session inspection.</td></tr><tr><td><a href="/ai-workflows/agent-skills.md"><strong>Agent Skills</strong></a></td><td>Set up Agent Browser in Claude Code, Cursor and other AI coding assistants.</td></tr><tr><td><a href="/ai-workflows/mcp.md"><strong>MCP Server</strong></a></td><td>Give MCP-compatible AI assistants a remote Agent Browser.</td></tr></tbody></table>

## Code examples

Below are basic examples to initialize a cloud-hosted browser session:

{% tabs %}
{% tab title="Python (Playwrigth)" %}

```python
from playwright.sync_api import sync_playwright

username = "USERNAME" # include any account suffix
password = "PASSWORD"
endpoint = "hb.oxylabs.io"
browser_url = f"wss://{username}:{password}@{endpoint}?p_cc=US"

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(browser_url)
    page = browser.new_page()
    page.goto("https://ip.oxylabs.io/location")
    print(page.title())
    browser.close()
```

{% endtab %}

{% tab title="JavaScript (Playwright)" %}

```javascript
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; //For testing only
import { chromium } from "playwright";

const username = "USERNAME";
const password = "PASSWORD";
const endpoint = "hb.oxylabs.io";
const browserUrl = `wss://${username}:${password}@${endpoint}?p_cc=US`;

(async () => {
    const browser = await chromium.connectOverCDP(browserUrl);
    const ctx = browser.contexts()[0] || (await browser.newContext());
    const page = ctx.pages()[0] || (await ctx.newPage());
    await page.goto("https://ip.oxylabs.io/location");
    console.log(await page.title());
    await browser.close();
})();
```

{% endtab %}

{% tab title="JavaScript (Puppeteer)" %}

```javascript
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; //For testing only
import puppeteer from "puppeteer";

const username = "USERNAME";
const password = "PASSWORD";
const endpoint = "hb.oxylabs.io";
const browserUrl = `wss://${username}:${password}@${endpoint}?p_cc=US`;

(async () => {
    const browser = await puppeteer.connect({ browserWSEndpoint: browserUrl });
    const page = await browser.newPage();
    await page.goto("https://ip.oxylabs.io/location");
    console.log(await page.title());
    await browser.close();
})();
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Note:** Examples use `USERNAME` and `PASSWORD` for readability only. In real projects, load them from environment variables (e.g. `process.env.OXYLABS_USERNAME` / `process.env.OXYLABS_PASSWORD`) via a `.env` file.
{% endhint %}

{% hint style="warning" %}
**JavaScript:** Use `NODE_TLS_REJECT_UNAUTHORIZED="0"` flag before requiring Playwright in Node.js if you encounter TLS/certificate errors. For local testing only. In production, scope trust to the specific connection or add the provider’s CA certificate to your trust store instead.
{% endhint %}

## Parameter reference <a href="#need-a-feature-enabled" id="need-a-feature-enabled"></a>

<table><thead><tr><th width="184">Parameter</th><th width="467">Description</th><th>Type</th></tr></thead><tbody><tr><td><code>p_cc</code></td><td>Country geolocation in <code>ISO 3166-1 alpha-2</code> 2-letter code (e.g., <code>US</code>, <code>DE</code>).</td><td>string</td></tr><tr><td><code>p_state</code></td><td>State geolocation in lowercase (e.g., <code>texas</code>). Overrides <code>p_cc</code> if both are used. <a href="https://content.gitbook.com/content/BQ7Zf9paoN3FTeGcyfY1/blobs/cVjpiu1GKicluiVIT9og/us_states.txt">Supported states list</a>.</td><td>string</td></tr><tr><td><code>p_city</code></td><td>City geolocation, lowercase, <code>_</code> for spaces (e.g., <code>new_york</code>). <code>p_cc</code> / <code>p_state</code> required.</td><td>string</td></tr><tr><td><code>p_device</code></td><td>Set device fingerprints, viewports, and user-agents. Supports <code>desktop</code> (default) and <code>mobile</code>.</td><td>string</td></tr><tr><td><code>p_src</code></td><td>Browser engine. Supports <code>oxybrowser</code> proprietary engine. If omitted, the standard engine is used. See <a href="/products/agent-browser/engine-and-fingerprints.md">Engine &#x26; Fingerprints</a>.</td><td>string</td></tr><tr><td><code>p_fp_tag</code></td><td>Fingerprint profile: <code>oxyfp_win</code> (default), <code>oxyfp_mac</code>, <code>oxyfp_mac_optimized</code> (only with <code>p_src=oxybrowser</code>), <code>oxyfp_win_v1</code> (legacy). See <a href="/products/agent-browser/engine-and-fingerprints.md">Engine &#x26; Fingerprints</a>.</td><td>string</td></tr><tr><td><code>page_handler</code></td><td>Identify pages and handle access challenges automatically. Default: <code>false</code>. See <a href="/products/agent-browser/page-handler.md">Page Handler</a>.</td><td>boolean</td></tr><tr><td><code>solve_captcha</code></td><td>Automatic, real-time CAPTCHA solving on page loads. Default: <code>false</code>.</td><td>boolean</td></tr><tr><td><code>record</code></td><td>Record headless session video. Default: <code>false</code>.</td><td>boolean</td></tr><tr><td><code>record_name</code></td><td>Name the recording for easy lookup (<code>^[a-zA-Z0-9_-]{1,64}$</code>).</td><td>string</td></tr><tr><td><code>session_name</code></td><td>Sticky sessions. Reconnects to the same live remote browser (<code>^[A-Za-z0-9-]{3,36}$</code>, supports hyphens, no underscores). Max TTL – 24h.</td><td>string</td></tr><tr><td><code>keep_alive</code></td><td>Keeps the remote browser instance live on client disconnect when set to <code>true</code>. Requires <code>session_name</code>. Default: <code>false</code>.</td><td>boolean</td></tr><tr><td><code>o_profile</code></td><td>Persistent profiles – save/restore cookies and <code>localStorage</code> with a named profile (<code>^[a-zA-Z0-9][a-zA-Z0-9_-]{2,35}$</code>).</td><td>string</td></tr><tr><td><code>o_profile_save</code></td><td>Persistent profiles – force profile save during or at the end of the session. Requires <code>o_profile</code>. Default: <code>false</code>.</td><td>boolean</td></tr><tr><td><code>proxy_resi_ses_id</code></td><td>Custom session ID to pin residential exit IP across sessions (<code>^[A-Za-z0-9]{3,36}$</code>).</td><td>string</td></tr><tr><td><code>proxy_resi_ses_time</code></td><td>Duration to hold the pinned residential proxy exit IP in minutes. Min <code>1</code>, max <code>1440</code> (24h).</td><td>integer</td></tr></tbody></table>

## Recommended setup

### Optimizing traffic

Scraping dynamic pages often causes the browser to download unnecessary assets such as heavy media, tracking scripts, images, and fonts. This consumes bandwidth and slows down execution times.

You can intercept and abort these requests programmatically before they consume resources:

{% tabs %}
{% tab title="Python (Playwright)" %}

```python
# Abort heavy assets to save bandwidth and improve speeds
def block_resources(route):
    if route.request.resource_type in ["image", "stylesheet", "media", "font"]:
        route.abort()
    else:
        route.continue_()

page.route("**/*", block_resources)
```

{% endtab %}

{% tab title="JavaScript (Playwright)" %}

```javascript
// Abort heavy assets to save bandwidth and improve speeds
await page.route("**/*", (route) => {
    const type = route.request().resourceType();
    if (["image", "stylesheet", "media", "font"].includes(type)) {
        return route.abort();
    }
    return route.continue();
});
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
If you use Page Handler, do not block stylesheets, images, media or fonts for the target. Blocking them can keep a challenge widget from loading.
{% endhint %}

### Error handling and retries <a href="#error-handling-and-retries" id="error-handling-and-retries"></a>

Network hiccups and 10-sessions-per-second rate limit mean a single `connectOverCDP` call can fail transiently. Wrap connection in a `retry` with exponential `backoff`, and cap each attempt with a timeout. The example below uses standard Playwright API.

```javascript
const { chromium } = require("playwright");

const MAX_RETRIES = 5;
const BASE_DELAY_MS = 1000;

async function connectWithRetry(endpoint, { maxRetries = MAX_RETRIES } = {}) {
    let lastError;
    for (let attempt = 0; attempt < maxRetries; attempt++) {
        try {
            // Cap how long a single connection attempt may take.
            return await chromium.connectOverCDP(endpoint, { timeout: 60_000 });
        } catch (err) {
            lastError = err;
            // Exponential backoff with jitter — backs off when you hit the
            // 10-new-sessions/second rate limit instead of hammering the endpoint.
            const delay = BASE_DELAY_MS * 2 ** attempt + Math.random() * 250;
            console.warn(
                `connect attempt ${attempt + 1} failed: ${err.message}; ` +
                `retrying in ${Math.round(delay)}ms`
            );
            await new Promise((resolve) => setTimeout(resolve, delay));
        }
    }
    throw lastError;
}
```

{% hint style="info" %}
For the full list of handshake, WebSocket close and in-page errors, see [Response Codes](/products/agent-browser/response-codes.md).
{% endhint %}

### Resource cleanup <a href="#resource-cleanup" id="resource-cleanup"></a>

Always close the browser when you are done, even if your automation throws midway. Use a `try { ... } finally { ... }` block so cleanup runs on every path:

```javascript
const browser = await connectWithRetry(endpoint);
try {
    const ctx = browser.contexts()[0] || (await browser.newContext());
    const page = ctx.pages()[0] || (await ctx.newPage());
    await page.goto("https://ip.oxylabs.io/location");
    // ... your automation ...
} finally {
    // Runs even if the block above throws. Without this, a session left open
    // after an exception lingers and counts against your 100 concurrent-session limit.
    await browser.close();
}
```

If an exception occurs between connect and close and you do not clean up, the remote session stays open and consumes one of your **100 concurrent sessions** until it is reclaimed. Leaking sessions under load can exhaust the limit and block new connections.

## Need a feature enabled? <a href="#need-a-feature-enabled" id="need-a-feature-enabled"></a>

Some Agent Browser features are limited by default. To adjust your limits, contact Oxylabs support ([live chat](https://oxylabs.io/) or [email](mailto:support@oxylabs.io)) or your Dedicated Account Manager:

* **More persistent profiles** – raise your account's default cap of 5 saved profiles.
* **More persistent sessions** – raise your account's default cap of 5 concurrent persistent sessions.
* **Restricted targets** – unlock more target categories via KYC process.
* **Higher rate limits** – increase beyond 100 concurrent sessions / 10 per second.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developers.oxylabs.io/products/agent-browser.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
