reCAPTCHA

How to Solve reCAPTCHA v3 in Python

reCAPTCHA v3 is harder to automate than v2. There is no visual challenge, but the returned token carries a risk score (0.0 to 1.0) that the target site uses to decide whether to allow or block the request. A token with a score of 0.3 is rejected by sites requiring 0.7+. This tutorial covers the parameters and patterns that maximize your token score.

For background on how reCAPTCHA v3 works, see the reCAPTCHA Guide.

Prerequisites

pip install requests playwright
playwright install chromium

Key Differences from reCAPTCHA v2

Aspect reCAPTCHA v2 reCAPTCHA v3
Visual challenge Yes (sometimes) Never
Output Token (pass/fail) Token + score (0.0–1.0)
Solver method userrecaptcha userrecaptcha + version=v3
Extra parameter None action, min_score
Token validity 2 minutes 2 minutes
Failure mode Invalid token Low score; action mismatch

Step 1 — Find the Site Key and Action Name

The v3 site key is embedded in the page the same way as v2. The action name is also important — it must match what the site's JavaScript passes to grecaptcha.execute(). If there's a mismatch, the server may reject the token even with a good score.

import re
import requests

def extract_recaptcha_v3_params(page_url: str) -> dict:
    """Extract v3 site key and action name from page source."""
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
    html = requests.get(page_url, headers=headers, timeout=15).text

    # Extract site key
    key_patterns = [
        r'data-sitekey=["\']([0-9A-Za-z_\-]{40,})["\']',
        r'"sitekey"\s*:\s*"([0-9A-Za-z_\-]{40,})"',
        r'grecaptcha\.execute\(["\']([0-9A-Za-z_\-]{40,})["\']',
    ]
    site_key = None
    for p in key_patterns:
        m = re.search(p, html)
        if m:
            site_key = m.group(1)
            break

    # Extract action name (e.g., 'login', 'submit', 'checkout')
    action_match = re.search(r'action\s*:\s*["\']([a-zA-Z0-9_/]+)["\']', html)
    action = action_match.group(1) if action_match else "verify"

    if not site_key:
        raise ValueError(f"reCAPTCHA v3 site key not found on {page_url}")

    return {"site_key": site_key, "action": action}

Step 2 — Solve reCAPTCHA v3

The critical parameters for a high-scoring token: - version=v3 — tells the solver this is a v3 task - action — must match the page's action name - min_score — minimum score you'll accept (0.3, 0.7, or 0.9)

import time

def solve_recaptcha_v3(
    api_key: str,
    page_url: str,
    site_key: str,
    action: str = "verify",
    min_score: float = 0.7,
) -> str:
    """
    Solve reCAPTCHA v3. Returns a scored token.

    min_score options: 0.3 (permissive), 0.7 (standard), 0.9 (strict)
    Most sites use 0.5 as their threshold; pass 0.7 for a safe margin.
    """
    payload = {
        "key": api_key,
        "method": "userrecaptcha",
        "version": "v3",
        "googlekey": site_key,
        "pageurl": page_url,
        "action": action,
        "min_score": min_score,
        "json": 1,
    }

    r = requests.post("https://ocr.captchaai.com/in.php", data=payload, timeout=30)
    r.raise_for_status()
    data = r.json()
    if data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {data}")
    task_id = data["request"]

    # v3 typically takes 10–20s — start polling at 8s
    time.sleep(8)
    for _ in range(24):
        r = requests.get(
            "https://ocr.captchaai.com/res.php",
            params={"key": api_key, "action": "get", "id": task_id, "json": 1},
            timeout=30,
        )
        data = r.json()
        if data.get("status") == 1:
            return data["request"]  # The scored v3 token
        time.sleep(5)

    raise TimeoutError("reCAPTCHA v3 solve timed out")

Step 3 — Inject and Submit

reCAPTCHA v3 tokens are injected the same way as v2 tokens. The difference is that the form submission triggers server-side score verification rather than a human-facing challenge.

from playwright.sync_api import sync_playwright

def submit_v3_form(page_url: str, api_key: str, action: str = "verify") -> None:
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.goto(page_url, wait_until="networkidle")

        params = extract_recaptcha_v3_params(page_url)
        token = solve_recaptcha_v3(
            api_key, page_url,
            site_key=params["site_key"],
            action=params.get("action", action),
            min_score=0.7,
        )

        # v3 stores the token in a hidden input or directly as a JS variable
        page.evaluate(f"""
            // Inject into any recaptcha response field
            document.querySelectorAll('[name="g-recaptcha-response"]')
                .forEach(el => {{ el.value = '{token}'; }});

            // Override grecaptcha.execute to return the pre-solved token
            if (window.grecaptcha && window.grecaptcha.execute) {{
                window.grecaptcha.execute = () => Promise.resolve('{token}');
            }}
        """)

        page.click('button[type="submit"]')
        page.wait_for_load_state("networkidle")
        browser.close()

Handling Sites with Strict Score Thresholds

Some sites (Google services, banking apps) require a score of 0.9. At this threshold, most solver implementations struggle. Strategies:

  1. Request min_score=0.9 from CaptchaAI — the solver will only return a result if it achieves that score. Expect longer solve times (15–30s) and some ERROR_CAPTCHA_UNSOLVABLE responses.

  2. Use a residential proxy for the page session — the origin IP is a significant factor in v3 scoring. Datacenter IPs start with a lower base score.

  3. Warm the session — load 2–3 pages on the target domain before the CAPTCHA page. v3 uses cumulative behavioral signals across the session.

def solve_with_session_warmup(api_key: str, base_url: str, captcha_url: str, site_key: str) -> str:
    """Load a few pages first to build a more legitimate session context."""
    from playwright.sync_api import sync_playwright
    import time

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        ctx = browser.new_context()
        page = ctx.new_page()

        # Warm the session
        page.goto(base_url, wait_until="networkidle")
        time.sleep(2)
        page.goto(captcha_url, wait_until="networkidle")

        # Now extract the live site key and solve
        live_key = page.get_attribute("[data-sitekey]", "data-sitekey") or site_key
        token = solve_recaptcha_v3(api_key, captcha_url, live_key, min_score=0.7)
        browser.close()

    return token

Common Errors and Fixes

Score too low / "action blocked" at target site - Request higher min_score (0.7 or 0.9) - Use residential proxy for the page session - Warm the session before reaching the CAPTCHA page

ERROR_CAPTCHA_UNSOLVABLE with high min_score - The solver couldn't achieve the requested score tier - Reduce min_score to 0.5 and check if the target site accepts 0.5+ tokens - Switch to a solver with better v3 implementation (CaptchaAI targets 0.7–0.9 by default)

Action name mismatch - The site's grecaptcha.execute('SITEKEY', {action: 'login'}) call uses a specific action - Extract the action dynamically and pass it to the solver - Wrong action name causes the token to fail server-side validation on action-aware sites

Token expires before submission - v3 tokens expire in 2 minutes — generate immediately before submit - In long-running Playwright sessions, generate the token as the last step before clicking submit

Production Readiness Notes

Use How to Solve reCAPTCHA v3 in Python as a decision and implementation aid, not just as a one-time reference. The practical test for how to solve recaptcha v3 python is whether the same approach behaves reliably when traffic is messy: rotating sessions, expired tokens, changing widget parameters, intermittent solver delays, and target pages that refresh without warning. For Automation developer, the safest rollout is to start with a narrow fixture, record every submitted task, and compare the solver response with the browser state that finally submits the form. That makes failures explainable instead of mysterious, especially when a target alternates between visible challenges, invisible checks, and server-side verification.

Evaluation Criteria

A how-to should be exercised against staging first, then promoted with feature flags so failed solves can fall back without blocking the entire workflow. For reCAPTCHA work, the most useful scorecard combines technical acceptance with operational cost. A low nominal price is not enough if retries double the real cost per accepted token, and a fast median solve time is not enough if p95 latency stalls the queue. Track these criteria before you standardize the workflow:

  • The challenge subtype, sitekey, action, rqdata, blob, captchaId, or page URL used for each task.
  • Median and p95 solve time, separated by provider and target domain.
  • Accepted-token rate on the target page, not just successful API responses.
  • Retry count, timeout count, zero-balance incidents, and invalid-parameter errors.
  • The exact browser, proxy region, and user-agent that submitted the solved token.

Rollout Checklist

Before this guidance moves into a production job, build a small acceptance suite around the pages that matter most. Run it with a fixed browser profile, then repeat with the proxy and concurrency settings you expect in production. Keep the first release conservative: bounded polling, clear timeout handling, and a fallback path when the solver cannot return a usable answer. For reCAPTCHA, watch score thresholds, hostname checks, action names, token age, and fallback behavior when Google returns a low-confidence response. That checklist keeps the article useful after the first copy-paste, because the integration is judged by end-to-end completion rather than by whether a code sample returned a string.

Monitoring Signals

Healthy CAPTCHA automation is observable. Log the task id, provider, challenge type, target host, queue time, solve time, final submit status, and normalized error code for every attempt. Review those logs in daily batches at first, then move to alerts once the baseline is stable. Sudden drops usually come from target-side changes: a new sitekey, a changed action name, a stricter hostname check, an added managed challenge, or a proxy pool that no longer matches the expected geography. When you can see those shifts quickly, provider switching becomes a controlled decision instead of a late-night rewrite.

Maintenance Cadence

Revisit the setup whenever the target UI changes, when the solver provider changes task names or pricing, or when benchmark data shows a sustained latency or solve-rate shift. Keep one known-good fixture for each CAPTCHA subtype and rerun it after dependency upgrades, browser updates, and proxy changes. If the article is used for vendor selection, repeat the same fixture across at least two providers before renewing a balance or migrating the whole pipeline. That habit keeps how to solve recaptcha v3 python work aligned with the real target behavior rather than with stale assumptions.

Comments are disabled for this article.

Related Posts

hCaptcha How to Solve hCaptcha in Python
Complete Python tutorial for solving h Captcha automatically — covers site key extraction, solver API integration with Captcha AI, token injection using Playwri...

Complete Python tutorial for solving h Captcha automatically — covers site key extraction, solver API integrat...

May 05, 2026
Developer Guides How to Use a CAPTCHA Solver with Playwright
Step-by-step guide to integrating a CAPTCHA solver into Playwright automation.

Step-by-step guide to integrating a CAPTCHA solver into Playwright automation. Covers re CAPTCHA v 2, h Captch...

May 06, 2026
GeeTest GeeTest Slider CAPTCHA Explained
How the Gee Test slider CAPTCHA works under the hood — challenge generation, browser-side trajectory scoring, and why it is harder to automate than it looks.

How the Gee Test slider CAPTCHA works under the hood — challenge generation, browser-side trajectory scoring,...

May 05, 2026