> ## Documentation Index
> Fetch the complete documentation index at: https://hyperbrowser.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CAPTCHA Solving

> Learn how to solve CAPTCHAs in your browser sessions

Hyperbrowser can automatically detect and solve CAPTCHAs when you enable it during session creation.

<Note>CAPTCHA solving requires being on a paid plan.</Note>

## Enable CAPTCHA Solving

Set the session creation parameter to enable solving:

<CodeGroup>
  ```typescript Node.js theme={null}
  import { Hyperbrowser } from "@hyperbrowser/sdk";
  import { config } from "dotenv";

  config();

  const client = new Hyperbrowser({
    apiKey: process.env.HYPERBROWSER_API_KEY,
  });

  async function main() {
    const session = await client.sessions.create({
      solveCaptchas: true,
    });

    try {
      console.log("Session Live URL:", session.liveUrl);
      console.log("WS Endpoint:", session.wsEndpoint);
      // ... connect with Playwright/Puppeteer and automate
    } finally {
      await client.sessions.stop(session.id);
    }
  }

  main().catch(console.error);
  ```

  ```python Python theme={null}
  import asyncio
  import os
  from dotenv import load_dotenv
  from hyperbrowser import AsyncHyperbrowser
  from hyperbrowser.models import CreateSessionParams

  # Load environment variables from .env file
  load_dotenv()

  client = AsyncHyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))


  async def main():
      # Create a session and connect to it using Pyppeteer
      session = await client.sessions.create(
          params=CreateSessionParams(
              solve_captchas=True,
          )
      )

      try:
          print(f"Session Live URL: {session.live_url}")
          print(f"WS Endpoint: {session.ws_endpoint}")
          # ... connect with Playwright/Puppeteer and automate
      except Exception as e:
          print(f"Error: {e}")
      finally:
          await client.sessions.stop(session.id)


  # Run the async main function
  if __name__ == "__main__":
      asyncio.run(main())
  ```
</CodeGroup>

<Warning>
  Some sites require proxies to be enabled for CAPTCHA solving to work reliably.
</Warning>

## Enable or Disable During a Session

You can start or stop automatic CAPTCHA solving on an active session without recreating the browser. This is useful when you only want solving enabled around specific pages or workflows.

<CodeGroup>
  ```typescript Node.js theme={null}
  await client.sessions.startCaptchaSolving(session.id, {
    solverType: "visual",
  });

  // ... navigate to pages that may contain CAPTCHAs

  await client.sessions.stopCaptchaSolving(session.id);
  ```

  ```python Python theme={null}
  from hyperbrowser.models import UpdateSessionSolveCaptchasParams

  await client.sessions.start_captcha_solving(
      session.id,
      UpdateSessionSolveCaptchasParams(solver_type="visual"),
  )

  # ... navigate to pages that may contain CAPTCHAs

  await client.sessions.stop_captcha_solving(session.id)
  ```

  ```bash cURL theme={null}
  curl -X PUT "https://api.hyperbrowser.ai/api/session/$SESSION_ID/update" \
    -H "Content-Type: application/json" \
    -H "x-api-key: $HYPERBROWSER_API_KEY" \
    -d '{
      "type": "solveCaptchas",
      "params": {
        "enabled": true,
        "solverType": "visual"
      }
    }'

  curl -X PUT "https://api.hyperbrowser.ai/api/session/$SESSION_ID/update" \
    -H "Content-Type: application/json" \
    -H "x-api-key: $HYPERBROWSER_API_KEY" \
    -d '{
      "type": "solveCaptchas",
      "params": {
        "enabled": false
      }
    }'
  ```
</CodeGroup>

<Note>
  Set `solverType` to `"visual"` to use the visual reCAPTCHA solver. If omitted, the session uses the default automatic CAPTCHA solver configuration.
</Note>

## Run Manual CAPTCHA Evaluation

You can also trigger a bounded CAPTCHA evaluation on an active session. This runs once against the current browser pages and returns the evaluation result.

<CodeGroup>
  ```typescript Node.js theme={null}
  const result = await client.sessions.evaluateCaptcha(session.id, {
    captchaType: "recaptcha",
    iterations: 2,
  });
  ```

  ```python Python theme={null}
  from hyperbrowser.models import CaptchaEvaluationParams

  result = await client.sessions.evaluate_captcha(
      session.id,
      CaptchaEvaluationParams(
          captcha_type="recaptcha",
          iterations=2,
      ),
  )
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.hyperbrowser.ai/api/session/$SESSION_ID/captcha/evaluate" \
    -H "Content-Type: application/json" \
    -H "x-api-key: $HYPERBROWSER_API_KEY" \
    -d '{
      "captchaType": "recaptcha",
      "iterations": 2
    }'
  ```
</CodeGroup>

<Note>
  Supported manual CAPTCHA targets are `turnstile`, `cloudflare-challenge`, `aliexpress`, `recaptcha`, and `amazon`.
</Note>

## Waiting for CAPTCHA Solve

Solving can take time. When navigating to pages that might contain CAPTCHAs, add appropriate waits:

```typescript Node.js theme={null}
const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms));

await page.goto("https://news.ycombinator.com/", { waitUntil: "networkidle0" });
await sleep(20_000);
```

A better approach is to wait on session events so you know exactly when a CAPTCHA is detected and when it is solved. Below is an example using Playwright, but you can adapt the same logic for Puppeteer or other libraries.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { Hyperbrowser } from "@hyperbrowser/sdk";
  import { config } from "dotenv";
  import { chromium } from "playwright";

  config();

  const client = new Hyperbrowser({
    apiKey: process.env.HYPERBROWSER_API_KEY,
  });

  const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

  const MAX_DETECTION_TRIES = 10;
  const MAX_SOLVED_TRIES = 30;

  const waitForCaptcha = async (sessionId: string, startTimestamp: number) => {
    let solved = false;
    let detected = false;
    let detectionTries = 0;
    let solvedTries = 0;

    console.log("Waiting for captcha detection...");
    while (!detected && detectionTries < MAX_DETECTION_TRIES) {
      const resp = await client.sessions.eventLogs.list(sessionId, {
        startTimestamp,
        types: ["captcha_detected"],
      });
      const data = resp.data;
      detected = data.length > 0;
      detectionTries++;
      await sleep(1000);
    }
    if (detected) {
      console.log("Captcha detected!");
    } else {
      console.log("Captcha not detected!");
      return;
    }

    console.log("Waiting for captcha solving...");
    while (!solved && solvedTries < MAX_SOLVED_TRIES) {
      const resp = await client.sessions.eventLogs.list(sessionId, {
        startTimestamp,
        types: ["captcha_solved"],
      });
      const data = resp.data;
      solved = data.length > 0;
      solvedTries++;
      await sleep(1000);
    }
    if (solved) {
      console.log("Captcha solved!");
    } else {
      console.log("Captcha not solved!");
    }
  };

  const main = async () => {
    const session = await client.sessions.create({
      solveCaptchas: true,
    });
    console.log("Session Live URL:", session.liveUrl);

    try {
      const browser = await chromium.connectOverCDP(session.wsEndpoint);
      const context = browser.contexts()[0];
      const page = context.pages()[0];
      const startTimestamp = Date.now();
      await page.goto("https://2captcha.com/demo/cloudflare-turnstile");

      await waitForCaptcha(session.id, startTimestamp);
      await sleep(5_000);
    } catch (err) {
      console.error(`Error: ${err}`);
    } finally {
      await client.sessions.stop(session.id);
    }
  };

  main();
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Lifecycle Management" icon="rotate" href="/sessions/lifecycle">
    Manage session lifecycle
  </Card>

  <Card title="Ad Blocking" icon="shield-alt" href="/sessions/ad-blocking">
    Block ads and trackers
  </Card>

  <Card title="Profiles" icon="user" href="/sessions/profiles">
    Persist cookies and local storage
  </Card>

  <Card title="Extensions" icon="puzzle-piece" href="/sessions/extensions">
    Load browser extensions
  </Card>
</CardGroup>
