Developer Guides

How to Use a CAPTCHA Solver with Selenium

Selenium is the most widely deployed browser automation framework, but it has no native CAPTCHA handling. This guide shows how to integrate a CAPTCHA solver API into your Selenium Python scripts — covering the solve request, token injection, and form submission patterns that work reliably across CAPTCHA types.

Prerequisites

pip install selenium requests
# Download the WebDriver matching your browser:
# Chrome: https://chromedriver.chromium.org/
# Firefox: https://github.com/mozilla/geckodriver/releases

Or use webdriver-manager to handle driver downloads automatically:

pip install webdriver-manager

Architecture: Solve Outside Selenium, Inject Inside

The key design principle for CAPTCHA solving with Selenium:

Solve the CAPTCHA outside the browser, then inject the token into the page.

Do not try to simulate the CAPTCHA interaction inside the browser. Instead: 1. Extract the CAPTCHA site key from the page 2. Call the solver API with the site key and page URL 3. Receive the token 4. Use driver.execute_script() to inject it into the hidden form field 5. Submit the form

This approach works because CAPTCHA systems only validate the token — they do not require the solving interaction to happen in the same browser session.

Setup

import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


def get_driver(headless: bool = True) -> webdriver.Chrome:
    options = webdriver.ChromeOptions()
    if headless:
        options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    return webdriver.Chrome(
        service=Service(ChromeDriverManager().install()),
        options=options,
    )

Solving reCAPTCHA v2 with Selenium

Step 1 — Extract the Site Key at Runtime

from selenium.webdriver.common.by import By
import re

def get_recaptcha_sitekey(driver) -> str:
    """Extract the reCAPTCHA site key from the current page."""
    # Try the data-sitekey attribute first
    elements = driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
    if elements:
        return elements[0].get_attribute("data-sitekey")

    # Fall back to JS source search
    html = driver.page_source
    match = re.search(r'data-sitekey=["\']([0-9A-Za-z_\-]{40,})["\']', html)
    if match:
        return match.group(1)

    raise ValueError("reCAPTCHA site key not found on page")

Step 2 — Request the Token from CaptchaAI

def solve_recaptcha_v2(api_key: str, page_url: str, site_key: str) -> str:
    """Solve reCAPTCHA v2 using CaptchaAI."""
    r = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30)
    r.raise_for_status()
    data = r.json()
    if data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {data}")
    task_id = data["request"]

    time.sleep(5)
    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"]
        time.sleep(5)
    raise TimeoutError("reCAPTCHA solve timed out")

Step 3 — Inject the Token and Submit

def inject_recaptcha_token(driver, token: str) -> None:
    """Inject g-recaptcha-response token into all matching fields on the page."""
    driver.execute_script("""
        var token = arguments[0];
        var fields = document.querySelectorAll(
            '#g-recaptcha-response, [name="g-recaptcha-response"], textarea[id*="recaptcha"]'
        );
        fields.forEach(function(f) {
            f.value = token;
            f.style.display = 'block';  // Unhide if display:none
        });
    """, token)


def submit_form_with_recaptcha(page_url: str, api_key: str) -> None:
    driver = get_driver()
    try:
        driver.get(page_url)
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey]"))
        )

        site_key = get_recaptcha_sitekey(driver)
        token = solve_recaptcha_v2(api_key, page_url, site_key)
        inject_recaptcha_token(driver, token)

        # Submit the form
        submit_btn = WebDriverWait(driver, 5).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]'))
        )
        submit_btn.click()
        WebDriverWait(driver, 15).until(EC.url_changes(page_url))
    finally:
        driver.quit()

Solving hCaptcha with Selenium

def get_hcaptcha_sitekey(driver) -> str:
    elements = driver.find_elements(By.CSS_SELECTOR, "[data-sitekey]")
    # hCaptcha sitekeys are UUIDs (8-4-4-4-12 format)
    for el in elements:
        sk = el.get_attribute("data-sitekey")
        if sk and len(sk) == 36 and sk.count("-") == 4:
            return sk
    raise ValueError("hCaptcha sitekey not found")


def solve_hcaptcha(api_key: str, page_url: str, site_key: str) -> str:
    r = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key, "method": "hcaptcha",
        "sitekey": site_key, "pageurl": page_url, "json": 1,
    }, timeout=30)
    r.raise_for_status()
    data = r.json()
    if data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {data}")
    task_id = data["request"]

    time.sleep(7)
    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"]
        time.sleep(5)
    raise TimeoutError("hCaptcha solve timed out")


def inject_hcaptcha_token(driver, token: str) -> None:
    driver.execute_script("""
        var token = arguments[0];
        document.querySelectorAll('[name="h-captcha-response"], textarea[name="h-captcha-response"]')
            .forEach(function(f) { f.value = token; });
    """, token)

Solving Cloudflare Turnstile with Selenium

def inject_turnstile_token(driver, token: str) -> None:
    driver.execute_script("""
        var token = arguments[0];
        document.querySelectorAll('[name="cf-turnstile-response"]')
            .forEach(function(f) { f.value = token; });
    """, token)

Common Issues with Selenium + CAPTCHA Solvers

Token injection succeeds but form still shows "CAPTCHA required"

Some implementations check for the token via a MutationObserver or callback. After injection, dispatch a change event:

driver.execute_script("""
    document.querySelectorAll('[name="g-recaptcha-response"]').forEach(function(f) {
        f.dispatchEvent(new Event('change', {bubbles: true}));
        f.dispatchEvent(new Event('input', {bubbles: true}));
    });
""")

StaleElementReferenceException when clicking submit after token injection

If the page re-renders after token injection, the button reference goes stale. Re-query it:

# Don't cache the button before token injection
inject_recaptcha_token(driver, token)
# Re-query after injection
submit_btn = driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]')
submit_btn.click()

Headless mode triggers harder CAPTCHAs

Some sites detect --headless flag via browser fingerprinting. Use --headless=new (Chrome 112+) and add these options to reduce detection signals:

options.add_argument("--headless=new")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_argument("--disable-blink-features=AutomationControlled")

reCAPTCHA v3 token immediately scoring below threshold

reCAPTCHA v3 scores the browser session — Selenium's default fingerprint is heavily flagged. The solve happens via API (no Selenium involvement) so the token score is independent of the browser session. The issue is usually at the site's server verification step. Ensure you're passing version=v3 and min_score=0.7 to the solver.

Using with Firefox / GeckoDriver

The solve-and-inject approach is fully browser-agnostic. Replace ChromeDriverManager with GeckoDriverManager and webdriver.Chrome with webdriver.Firefox — all token injection via execute_script() works identically.

Production Readiness Notes

Use How to Use a CAPTCHA Solver with Selenium as a decision and implementation aid, not just as a one-time reference. The practical test for captcha solver selenium 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 developer guide should become a reusable integration module with typed configuration, bounded polling, structured errors, and a single place for API credentials. For developer integration 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 developer integration, treat the code as a production pattern: timeouts, retries, logging, secret storage, and test fixtures matter as much as the solve request itself. 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 captcha solver selenium work aligned with the real target behavior rather than with stale assumptions.

Comments are disabled for this article.

Related Posts

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
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
reCAPTCHA How to Solve reCAPTCHA v2 in Python
Complete Python tutorial for solving re CAPTCHA v 2 (checkbox and invisible) automatically — includes site key extraction, solver API integration, token injecti...

Complete Python tutorial for solving re CAPTCHA v 2 (checkbox and invisible) automatically — includes site key...

May 05, 2026