Skip to main content
When you enable CAPTCHA solving in your project, CAPTCHAs are solved automatically in the background. However, your automation may need to wait for a CAPTCHA to be solved before proceeding, or know when a CAPTCHA was solved. These helpers let you wait for CAPTCHAs to be solved and react to status changes.
CAPTCHA is a general term for challenges that verify you’re human. This includes reCAPTCHA, hCaptcha, Cloudflare Turnstile, and similar services.

Available helpers

Function reference

wait_for_captcha_solve

Wait for a CAPTCHA to be solved. Supports three usage patterns: callable, wrapper, and decorator. Note: The decorator pattern is available in Python only. TypeScript uses direct call and wrapper patterns.
from intuned_runtime.captcha import wait_for_captcha_solve

async def wait_for_captcha_solve(
    page: Page,
    timeout_s: float = 10.0,
    settle_period: float = 5.0,
) -> None: ...
Parameters
page
Page
required
Playwright page object.
timeout_s
float
default:"10.0"
Maximum wait time in seconds. Raises TimeoutError if exceeded.
settle_period
float
default:"5.0"
Wait time in seconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.
ReturnsReturns None when solved or settle period elapses.

Raises
  • TimeoutError — Raised when timeout_s elapses while CAPTCHAs are still being solved.
  • CaptchaSolveError — Raised when the CAPTCHA solver fails. Contains a CaptchaError with the error code and details.
  • RuntimeError — Raised when the subscription cannot be created or the page context is invalid.

Examples
from intuned_runtime.captcha import wait_for_captcha_solve
from playwright.async_api import Page

async def automation(page: Page, params, **_kwargs):
    await page.goto("https://www.scrapingcourse.com/login/cf-turnstile")
    await wait_for_captcha_solve(page, timeout_s=10.0, settle_period=5.0)

on_captcha_event

from intuned_runtime.captcha import on_captcha_event, Captcha, CaptchaStatus
from playwright.async_api import Page

async def on_captcha_event(
    page: Page,
    status: CaptchaStatus,
    f: Callable[[Captcha], Awaitable[None] | None],
) -> None: ...
Register a callback for CAPTCHA updates. Subscribe to CAPTCHA updates on a page. The callback fires every time a CAPTCHA with the specified status is observed. The subscription remains active until the page or context is destroyed. Use this for monitoring, logging, or reacting to CAPTCHA status.
Parameters
page
Page
required
Playwright page object.
status
required
The CAPTCHA status to listen for.
f
required
Callback function that executes when the status is observed. Receives a Captcha instance as its only argument.

Returns Returns None. The function subscribes and returns immediately. The callback fires each time a CAPTCHA with the specified status is observed.
Raises
  • RuntimeError — Raised when the subscription cannot be created or the page context is invalid.
Example
from intuned_runtime.captcha import on_captcha_event, Captcha
from playwright.async_api import Page


async def handle_captcha_event(captcha: Captcha) -> None:
    print(f"Captcha {captcha.id} (tab={captcha.tab_id}) status={captcha.status} retries={captcha.retry_count}")

    if captcha.status == "solved":
        print(f"Solved after {captcha.retry_count} retries")
    elif captcha.status == "error":
        print(f"Solve error: {captcha.error}")


await on_captcha_event(page, status='solved', f=handle_captcha_event)

once_captcha_event

from intuned_runtime.captcha import once_captcha_event, Captcha, CaptchaStatus
from playwright.async_api import Page

async def once_captcha_event(
    page: Page,
    status: CaptchaStatus,
    f: Callable[[Captcha], Awaitable[None] | None],
) -> None: ...
Register a one-time callback for a CAPTCHA update. Subscribe to CAPTCHA updates on a page. The callback fires once when a CAPTCHA with the specified status is observed, then automatically unsubscribes. Use this when you need to respond only to the next occurrence, such as recording when a CAPTCHA is solved or performing cleanup.
Parameters
page
Page
required
Playwright page object.
status
required
The CAPTCHA status to listen for.
f
required
Callback function that executes when the status is observed. Receives a Captcha instance as its only argument.

Returns Returns None. The function subscribes and returns immediately. The callback fires at most once.
Raises
  • RuntimeError — Raised when the subscription cannot be created or the page context is invalid.
Example
from intuned_runtime.captcha import once_captcha_event, Captcha
from playwright.async_api import Page


async def handle_solve_once(captcha: Captcha) -> None:
    print(f"One-time notify: captcha {captcha.id} status={captcha.status} retries={captcha.retry_count}")


await once_captcha_event(page, status='solved', f=handle_solve_once)

remove_captcha_event_listener

from intuned_runtime.captcha import remove_captcha_event_listener, Captcha, CaptchaStatus
from playwright.async_api import Page
from typing import Callable, Awaitable

async def remove_captcha_event_listener(
    page: Page,
    status: CaptchaStatus,
    f: Callable[[Captcha], Awaitable[None] | None],
) -> None: ...
Remove a previously registered CAPTCHA callback. Unsubscribe a callback registered using on_captcha_event or once_captcha_event. You must pass the same page, status, and callback function that were used to register the callback.
Parameters
page
Page
required
Playwright page object.
status
CaptchaStatus
required
The CAPTCHA status that the listener was registered for.
f
Callable[[Captcha], Awaitable[None] | None]
required
The callback function that was originally registered. Must be the same function reference.

Returns Returns None. The function unsubscribes the callback and returns immediately.
Raises
  • RuntimeError — Raised when the callback cannot be removed or the page context is invalid.
Example
from intuned_runtime.captcha import on_captcha_event, remove_captcha_event_listener, Captcha
from playwright.async_api import Page


async def handle_captcha_event(captcha: Captcha) -> None:
    print(f"Captcha status: {captcha.status}")


# Subscribe to CAPTCHA updates
await on_captcha_event(page, status='solved', f=handle_captcha_event)

# Later, unsubscribe
await remove_captcha_event_listener(page, status='solved', f=handle_captcha_event)
Note: The callback function reference must match exactly. Anonymous functions cannot be removed. Callbacks registered with once_captcha_event are automatically removed after firing.

Best practices

  • Use wait_for_captcha_solve for most cases where you need to wait for solve completion.
  • Set appropriate timeout values based on CAPTCHA complexity (15-30 seconds typical).
  • Adjust the settle period for the wait before checking status. Resets when CAPTCHAs are detected.
  • Enable wait_for_network_settled to wait for network idle before starting solve operations.
  • Subscribe to CAPTCHA updates using on_captcha_event or once_captcha_event for telemetry and monitoring.
  • Store callback function references if you need to unsubscribe later.

Type reference

Captcha

from pydantic import BaseModel, Field
from intuned_runtime.captcha import Captcha, CaptchaStatus, CaptchaError

class Captcha(BaseModel):
    model_config = {
        "populate_by_name": True,
        "serialize_by_alias": True,
    }
    id: str
    tab_id: int = Field(alias="tabId", default=0)
    type: str
    status: CaptchaStatus
    retry_count: int = Field(alias="retryCount", default=0)
    error: CaptchaError | None = None
Properties
id
str
required
Unique identifier for the CAPTCHA observation.
tab_id
int
required
Browser tab ID where the CAPTCHA was detected.
type
str
required
CAPTCHA provider type, such as recaptcha, hcaptcha, or cloudflare.
status
required
Current solving state.
retry_count
int
Number of solve attempts made. Defaults to 0.
error
Error details when status is error, otherwise None.

CaptchaStatus

from typing import Literal
from intuned_runtime.captcha import CaptchaStatus

CaptchaStatus = Literal["attached", "solving", "solved", "error", "detached"]
Values
  • attached — CAPTCHA element detected in the page. Use this to know when a challenge appears.
  • solving — Solver is actively processing the CAPTCHA.
  • solved — CAPTCHA solved successfully. Resume your workflow.
  • error — Solver failed. Check the error field for details.
  • detached — CAPTCHA element removed from the page. Treat as cancelled.

CaptchaError

from pydantic import BaseModel
from typing import Any
from intuned_runtime.captcha import CaptchaError, CaptchaErrorCode

class CaptchaError(BaseModel):
    code: CaptchaErrorCode
    error: Any | None = None
Properties
code
required
Error code indicating the type of failure.
error
Any | None
Additional error details, if available.

CaptchaErrorCode

from typing import Literal
from intuned_runtime.captcha import CaptchaErrorCode

CaptchaErrorCode = Literal[
    "HIT_LIMIT",
    "MAX_RETRIES",
    "UNEXPECTED_ERROR",
    "UNEXPECTED_SERVER_RESPONSE"
]
Values
  • HIT_LIMIT — Reached billing limits for CAPTCHA solves. See Plans and billing for details on limits and upgrading.
  • MAX_RETRIES — Exceeded maximum retry attempts specified in settings.maxRetries.
  • UNEXPECTED_ERROR — An unexpected error occurred while solving. This is a solver error and not related to your automation.
  • UNEXPECTED_SERVER_RESPONSE — The solver received an unexpected response. This is a solver error and not related to your automation.