> 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/headless-browser/captcha-handling.md).

# CAPTCHA Handling

By default, Oxylabs Headless Browser automatically detects and handles CAPTCHAs as soon as a page loads. No configuration or parameter setup is required.&#x20;

If a target site displays challenges dynamically during multi-step interactions, or if you require advanced event monitoring and speed optimizations, you can use the parameters, events, and techniques documented below.

{% hint style="info" %}
**Note:** Access success rates are highly target-dependent. Outcomes may vary based on the target site's specific configuration, enforcement levels (e.g., passive fingerprint tracking vs. active challenge escalation), and the real-time reputation of the session's residential IP.
{% endhint %}

## Monitoring CAPTCHA events

You can monitor the solver's lifecycle. The internal `oxylabs-runtime` browser extension broadcasts status events directly to the browser's `window` object. By registering a custom "message" event listener, your script can trace these events to pause and resume actions.

<table><thead><tr><th width="243">Event type</th><th>Description</th></tr></thead><tbody><tr><td><code>oxylabs-captcha-start</code></td><td>Solver has detected a CAPTCHA and initiated the handling process.</td></tr><tr><td><code>oxylabs-captcha-end</code></td><td>Solver completed successfully and handled the challenge.</td></tr><tr><td><code>oxylabs-captcha-solve-end</code></td><td>Alternate completion event emitted after success.</td></tr><tr><td><code>oxylabs-captcha-error</code></td><td>Solver failed to handle the challenge.</td></tr></tbody></table>

{% hint style="info" %}
Auto-solving is **enabled by default**. If you append `solve_captcha=false` to your connection URL, automatic handling is turned off and these events will not be emitted.
{% endhint %}

### Code examples

These examples show how to inject an `init` script to capture runtime CAPTCHA events before page navigation so your script pauses during active handling.

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

```python
from playwright.sync_api import sync_playwright

username = "USERNAME_abc12"
password = "PASSWORD"
endpoint = "ubc.oxylabs.io"
# Ensure solve_captcha=true is active (default) to emit events
browser_url = f"wss://{username}:{password}@{endpoint}?solve_captcha=true"

def run():
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(browser_url)
        ctx = browser.contexts[0]
        page = ctx.new_page()

        # Inject listener BEFORE navigation
        ctx.add_init_script("""
            window.addEventListener("message", (event) => {
                if (event.data && event.data.source === "oxylabs-runtime") {
                    window.__extensionStatus = event.data.type;
                }
            });
        """)

        page.goto("https://example.com/captcha-page", wait_until="domcontentloaded")

        # Wait for solver to complete (polls status flag)
        page.wait_for_function(
            """() => {
                const status = window.__extensionStatus;
                if (status === "oxylabs-captcha-error") {
                    throw new Error("CAPTCHA solving failed");
                }
                return status === "oxylabs-captcha-solve-end" || status === "oxylabs-captcha-end";
            }""",
            timeout=60000
        )
        print("CAPTCHA solved. Resuming automation...")
        browser.close()

if __name__ == "__main__":
    run()
```

{% endtab %}

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

```javascript
import { chromium } from "playwright";

const username = "USERNAME_abc12";
const password = "PASSWORD";
const endpoint = "ubc.oxylabs.io";
const browserUrl = `wss://${username}:${password}@${endpoint}?solve_captcha=true`;

(async () => {
    const browser = await chromium.connectOverCDP(browserUrl);
    const ctx = browser.contexts()[0];
    const page = await ctx.newPage();

    // Register init script to listen for oxylabs-runtime events
    await ctx.addInitScript(() => {
        window.addEventListener("message", (e) => {
            if (e.data?.source === "oxylabs-runtime") {
                window.__extensionStatus = e.data.type;
            }
        });
    });

    await page.goto("https://example.com/captcha-page", { waitUntil: "domcontentloaded" });

    try {
        // Halt script execution until CAPTCHA returns positive solve status
        await page.waitForFunction(() => {
            const status = window.__extensionStatus;
            if (status === "oxylabs-captcha-error") {
                throw new Error("CAPTCHA solving failed");
            }
            return status === "oxylabs-captcha-solve-end" || status === "oxylabs-captcha-end";
        }, null, { timeout: 60000 });

        console.log("CAPTCHA solved. Resuming automation...");
    } catch (err) {
        console.error("Error during bypass execution:", err.message);
    } finally {
        await browser.close();
    }
})();
```

{% endtab %}

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

```javascript
import puppeteer from "puppeteer";

const username = "USERNAME_abc12";
const password = "PASSWORD";
const endpoint = "ubc.oxylabs.io";
const browserUrl = `wss://${username}:${password}@${endpoint}?solve_captcha=true`;

(async () => {
    const browser = await puppeteer.connect({ browserWSEndpoint: browserUrl });
    const page = await browser.newPage();

    // Puppeteer alternative: Evaluate on document creation
    await page.evaluateOnNewDocument(() => {
        window.addEventListener("message", (e) => {
            if (e.data?.source === "oxylabs-runtime") {
                window.__extensionStatus = e.data.type;
            }
        });
    });

    await page.goto("https://example.com/captcha-page", { waitUntil: "domcontentloaded" });

    try {
        await page.waitForFunction(() => {
            const status = window.__extensionStatus;
            if (status === "oxylabs-captcha-error") {
                throw new Error("CAPTCHA solving failed");
            }
            return status === "oxylabs-captcha-solve-end" || status === "oxylabs-captcha-end";
        }, { timeout: 60000 });

        console.log("CAPTCHA solved. Resuming automation...");
    } catch (err) {
        console.error("Error during bypass execution:", err.message);
    } finally {
        await browser.close();
    }
})();
```

{% endtab %}
{% endtabs %}

## Dynamic manual triggers

Some targets only display CAPTCHA challenges following user actions (e.g., after clicking an interactive form submission button or performing dynamic scrolling).&#x20;

You can trigger the Headless Browser's CAPTCHA handler programmatically at any point in your session using `window.postMessage` event directly to the window object:

```
window.postMessage({ action: "solve_captcha", type: "type_name" }, "*");
```

{% hint style="info" %}
To learn more about supported CAPTCHA types, contact Oxylabs support ([live chat](https://oxylabs.io/) or [email](mailto:support@oxylabs.io)) or your Dedicated Account Manager.
{% endhint %}

### Standard trigger pattern

For typical elements, trigger the solver immediately after executing the user action:

```javascript
// Perform form interaction
await page.click("#form-submit-button");

// Trigger solving for late-stage CAPTCHA
await page.evaluate(() => {
    window.postMessage({ action: "solve_captcha", type: "type_name" }, "*");
});
```

{% hint style="info" %}
To learn more about supported CAPTCHA types or exceptions for specific types, contact Oxylabs support ([live chat](https://oxylabs.io/) or [email](mailto:support@oxylabs.io)) or your Dedicated Account Manager for more details.
{% endhint %}

## Speed optimization

Handling CAPTCHAs adds mechanical execution latency. When scraping targets that enforce repetitive CAPTCHA gates, consider these methods for better performance:

<table data-header-hidden><thead><tr><th width="175"></th><th></th></tr></thead><tbody><tr><td><strong>Reuse sessions</strong></td><td>Once a remote browser session completes a CAPTCHA on a domain, the verification is saved. Navigate repeatedly or perform additional page steps within the same browser session without re-triggering challenge solving routines.</td></tr><tr><td><strong>Multi-tab</strong> </td><td>Instead separate WebSocket connections (which launch distinct sandboxes with empty cookie pools), execute concurrent tasks by opening multiple tabs or contexts inside your existing session, to reduce connection and verification time across all pages.</td></tr><tr><td><strong>Adaptive disabling</strong></td><td>Once a reliable session trust signal has been established, disable the background solver checks with <code>solve_captcha=false</code>, to avoid process initialization latency for domains where your sessions already pass freely.</td></tr></tbody></table>


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.oxylabs.io/products/headless-browser/captcha-handling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
