Tutorials

CAPTCHA Handling in Mobile Apps with Appium

Mobile apps frequently embed CAPTCHAs in WebViews — reCAPTCHA, Turnstile, or hCaptcha rendered inside a native app. Appium can switch into these WebViews, extract sitekeys, and inject solved tokens from CaptchaAI.


How CAPTCHAs appear in mobile apps

Pattern Description Approach
WebView CAPTCHA reCAPTCHA/Turnstile in an embedded WebView Switch to WebView context, extract sitekey
Browser redirect App opens system browser for auth Use Appium to read browser URL, solve externally
Native CAPTCHA Custom image CAPTCHA rendered natively Screenshot and use Image/OCR solver

Most mobile CAPTCHAs are WebView-based — the same reCAPTCHA used on the web.


Setup: Appium with WebView debugging

Prerequisites

# Android: Enable WebView debugging in the app
# (Developers must set WebView.setWebContentsDebuggingEnabled(true))
# Or use a debug build of the app

# Install Appium
npm install -g appium
appium driver install uiautomator2  # Android
appium driver install xcuitest      # iOS

Appium capabilities

# Python
from appium import webdriver
from appium.options.android import UiAutomator2Options

options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "emulator-5554"
options.app = "/path/to/app.apk"
options.auto_grant_permissions = True

driver = webdriver.Remote("http://localhost:4723", options=options)

Step 1: Find and switch to the WebView

import time

# Wait for WebView to load
time.sleep(5)

# List available contexts
contexts = driver.contexts
print(f"Contexts: {contexts}")
# ['NATIVE_APP', 'WEBVIEW_com.example.app']

# Switch to WebView
webview_context = [c for c in contexts if "WEBVIEW" in c]
if webview_context:
    driver.switch_to.context(webview_context[0])
    print(f"Switched to: {webview_context[0]}")

JavaScript (WebdriverIO)

const { remote } = require('webdriverio');

const driver = await remote({
  capabilities: {
    platformName: 'Android',
    'appium:deviceName': 'emulator-5554',
    'appium:app': '/path/to/app.apk',
    'appium:automationName': 'UiAutomator2',
  },
});

// Wait and switch to WebView
await driver.pause(5000);
const contexts = await driver.getContexts();
const webview = contexts.find(c => c.includes('WEBVIEW'));
if (webview) {
  await driver.switchContext(webview);
}

Step 2: Extract the sitekey

Once in the WebView context, you have full DOM access:

# In WebView context — same as browser automation
sitekey = driver.execute_script("""
    // reCAPTCHA
    const recaptcha = document.querySelector('.g-recaptcha');
    if (recaptcha) return { type: 'recaptcha', sitekey: recaptcha.getAttribute('data-sitekey') };

    // Turnstile
    const turnstile = document.querySelector('.cf-turnstile');
    if (turnstile) return { type: 'turnstile', sitekey: turnstile.getAttribute('data-sitekey') };

    // hCaptcha
    const hcaptcha = document.querySelector('.h-captcha');
    if (hcaptcha) return { type: 'hcaptcha', sitekey: hcaptcha.getAttribute('data-sitekey') };

    return null;
""")

page_url = driver.current_url
print(f"Type: {sitekey['type']}, Key: {sitekey['sitekey']}, URL: {page_url}")

Step 3: Solve with CaptchaAI

import requests

API_KEY = "YOUR_API_KEY"

method_map = {
    "recaptcha": "userrecaptcha",
    "turnstile": "turnstile",
    "hcaptcha": "hcaptcha",
}

# Build submit params
submit_data = {
    "key": API_KEY,
    "method": method_map[sitekey["type"]],
    "pageurl": page_url,
    "json": "1",
}

# Type-specific key parameter
if sitekey["type"] == "recaptcha":
    submit_data["googlekey"] = sitekey["sitekey"]
else:
    submit_data["sitekey"] = sitekey["sitekey"]

resp = requests.post("https://ocr.captchaai.com/in.php", data=submit_data).json()
task_id = resp["request"]

# Poll
for _ in range(24):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()

    if result["status"] == 1:
        token = result["request"]
        print(f"Token: {token[:50]}...")
        break
    if result["request"] != "CAPCHA_NOT_READY":
        raise Exception(f"Error: {result['request']}")

Step 4: Inject the token

# Still in WebView context
token_field_map = {
    "recaptcha": "g-recaptcha-response",
    "turnstile": "cf-turnstile-response",
    "hcaptcha": "h-captcha-response",
}

field_name = token_field_map[sitekey["type"]]

driver.execute_script(f"""
    // Set the hidden input
    const input = document.querySelector('textarea[name="{field_name}"], input[name="{field_name}"]');
    if (input) {{
        input.value = arguments[0];
        input.style.display = 'block';
    }}

    // Trigger callback if exists
    const widget = document.querySelector('.g-recaptcha, .cf-turnstile, .h-captcha');
    const callback = widget?.getAttribute('data-callback');
    if (callback && typeof window[callback] === 'function') {{
        window[callback](arguments[0]);
    }}
""", token)

# Submit the form
driver.execute_script("document.querySelector('form').submit()")

# Switch back to native context
driver.switch_to.context("NATIVE_APP")

Handling native image CAPTCHAs

If the CAPTCHA is rendered natively (not in a WebView), screenshot and use Image/OCR:

import base64

# Switch to native context
driver.switch_to.context("NATIVE_APP")

# Find and screenshot the CAPTCHA element
captcha_element = driver.find_element("id", "captcha_image")
screenshot_b64 = captcha_element.screenshot_as_base64

# Submit to CaptchaAI as image
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "base64",
    "body": screenshot_b64,
    "json": "1",
}).json()

task_id = resp["request"]

# Poll and get text answer
for _ in range(20):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()
    if result["status"] == 1:
        answer = result["request"]
        break

# Type the answer into the input field
captcha_input = driver.find_element("id", "captcha_input")
captcha_input.send_keys(answer)

Troubleshooting

Problem Cause Fix
No WEBVIEW context WebView debugging not enabled Use a debug build or set setWebContentsDebuggingEnabled(true)
Can't find sitekey CAPTCHA loaded dynamically Add wait before extraction; use MutationObserver
Token injection fails Wrong field name Check the CAPTCHA type and corresponding input name
Context switch fails Multiple WebViews List all contexts and select the correct one

FAQ

Does this work on iOS?

Yes. Use the xcuitest driver. iOS WebViews are automatically debuggable in simulator and development-signed apps.

What if the app uses a custom browser?

If the app opens Chrome Custom Tabs or SFSafariViewController, Appium can't access the content. Use a proxy to intercept the CAPTCHA parameters instead.


Solve mobile CAPTCHAs with CaptchaAI

Get your API key at captchaai.com.


Discussions (0)

No comments yet.

Related Posts

Reference CaptchaAI CLI Tool: Command-Line CAPTCHA Solving and Testing
A reference for building and using a Captcha AI command-line tool — solve CAPTCHAs, check balance, test parameters, and integrate with shell scripts and CI/CD p...

A reference for building and using a Captcha AI command-line tool — solve CAPTCHAs, check balance, test parame...

Python Automation All CAPTCHA Types
Feb 26, 2026
Tutorials Testing CaptchaAI Before Full Migration: Parallel Run Guide
Run your existing CAPTCHA provider alongside Captcha AI in parallel — compare solve rates, speed, and cost before committing to a full migration.

Run your existing CAPTCHA provider alongside Captcha AI in parallel — compare solve rates, speed, and cost bef...

Python Automation All CAPTCHA Types
Feb 02, 2026
Tutorials Load Testing Your CAPTCHA Solving Pipeline with CaptchaAI
Load test your CAPTCHA solving pipeline to find breaking points — measure throughput, error rates, and resource usage under increasing concurrency.

Load test your CAPTCHA solving pipeline to find breaking points — measure throughput, error rates, and resourc...

Python Automation All CAPTCHA Types
Feb 02, 2026
DevOps & Scaling Blue-Green Deployment for CAPTCHA Solving Infrastructure
Implement blue-green deployments for CAPTCHA solving infrastructure — zero-downtime upgrades, traffic switching, and rollback strategies with Captcha AI.

Implement blue-green deployments for CAPTCHA solving infrastructure — zero-downtime upgrades, traffic switchin...

Python Automation All CAPTCHA Types
Apr 07, 2026
Reference CAPTCHA Solving Performance by Region: Latency Analysis
Analyze how geographic region affects Captcha AI solve times — network latency, proxy location, and optimization strategies for global deployments.

Analyze how geographic region affects Captcha AI solve times — network latency, proxy location, and optimizati...

Python Automation All CAPTCHA Types
Apr 05, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Python Automation Cloudflare Turnstile
Apr 08, 2026
DevOps & Scaling Ansible Playbooks for CaptchaAI Worker Deployment
Deploy and manage Captcha AI workers with Ansible — playbooks for provisioning, configuration, rolling updates, and health checks across your server fleet.

Deploy and manage Captcha AI workers with Ansible — playbooks for provisioning, configuration, rolling updates...

Python Automation All CAPTCHA Types
Apr 07, 2026
Tutorials Streaming Batch Results: Processing CAPTCHA Solutions as They Arrive
Process CAPTCHA solutions the moment they arrive instead of waiting for tasks to complete — use async generators, event emitters, and callback patterns for stre...

Process CAPTCHA solutions the moment they arrive instead of waiting for all tasks to complete — use async gene...

Python Automation All CAPTCHA Types
Apr 07, 2026
Tutorials Bulkhead Pattern: Isolating CAPTCHA Solving Failures
Apply the bulkhead pattern to isolate CAPTCHA solving failures — partition resources into independent pools so a slow or failing solver type doesn't starve othe...

Apply the bulkhead pattern to isolate CAPTCHA solving failures — partition resources into independent pools so...

Python Automation All CAPTCHA Types
Apr 07, 2026
API Tutorials Graceful Degradation When CAPTCHA Solving Fails
Keep your automation running when CAPTCHA solving fails — fallback strategies, queue-based retries, and degraded-mode patterns.

Keep your automation running when CAPTCHA solving fails — fallback strategies, queue-based retries, and degrad...

Python Automation All CAPTCHA Types
Apr 06, 2026
Tutorials Handling Multiple CAPTCHAs on a Single Page
how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI.

Learn how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI. Covers multi-iframe ext...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 09, 2026
Tutorials Extracting reCAPTCHA Parameters from Page Source
Extract re CAPTCHA parameters from any web page — sitekey, action, data-s, enterprise flag, and version — using regex, DOM queries, and network interception.

Extract all re CAPTCHA parameters from any web page — sitekey, action, data-s, enterprise flag, and version —...

Python reCAPTCHA v2 Web Scraping
Apr 07, 2026