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.
Callable
Wrapper
Decorator
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 Maximum wait time in seconds. Raises TimeoutError if exceeded.
Wait time in seconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.
Returns Returns None when solved or settle period elapses. from intuned_runtime.captcha import wait_for_captcha_solve
async def wait_for_captcha_solve (
page : Page,
func : Callable[ ... , Any],
timeout_s : float = 10.0 ,
settle_period : float = 5.0 ,
wait_for_network_settled : bool = True ,
) -> Any: ...
Parameters func
Callable[..., Any]
required
Function to execute before waiting for solve. Can be sync or async.
Maximum wait time in seconds. Raises TimeoutError if exceeded.
Wait time in seconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.
Whether to wait for network idle before starting the wait for solve operation.
Returns Returns the result of func, or None if func has no return value. from intuned_runtime.captcha import wait_for_captcha_solve
def wait_for_captcha_solve (
timeout_s : float = 10.0 ,
settle_period : float = 5.0 ,
wait_for_network_settled : bool = True ,
): ...
Parameters Maximum wait time in seconds. Raises TimeoutError if exceeded.
Wait time in seconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.
Whether to wait for network idle before starting the wait for solve operation.
Returns Preserves the wrapped function’s return type. The decorated function must accept a Page as its first argument.
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
Wait after navigating to a page with CAPTCHA
Execute action then wait for CAPTCHA solve
Reusable decorator for form submission
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
The CAPTCHA status to listen for.
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
Log CAPTCHA status changes
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
The CAPTCHA status to listen for.
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
One-time notification on solve
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
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
Subscribe and unsubscribe
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
Unique identifier for the CAPTCHA observation.
Browser tab ID where the CAPTCHA was detected.
CAPTCHA provider type, such as recaptcha, hcaptcha, or cloudflare.
Number of solve attempts made. Defaults to 0.
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
Error code indicating the type of failure.
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.