Mobile browser automation encounters the same CAPTCHAs as desktop — reCAPTCHA checkboxes, Cloudflare challenges, image grids — but also faces mobile-specific quirks like responsive layouts, touch-only interactions, and different User-Agent fingerprints. CaptchaAI handles the solving regardless of the device context.
This tutorial covers two approaches: Selenium with Chrome for Android via ADB, and Playwright's mobile emulation on desktop.
Real-World Scenario
You are automating form submissions on a mobile-optimized website that serves reCAPTCHA v2. The site detects mobile User-Agents and renders a mobile-specific CAPTCHA layout. You need to solve the CAPTCHA and submit the form from a mobile browser context.
Approach 1: Playwright Mobile Emulation (Python)
Playwright's device emulation is the simplest way to automate mobile browsers with CAPTCHA solving — no physical device required:
# playwright_mobile_captcha.py
import asyncio
import httpx
from playwright.async_api import async_playwright
API_KEY = "YOUR_API_KEY"
async def solve_recaptcha(sitekey: str, pageurl: str) -> str:
"""Submit reCAPTCHA v2 to CaptchaAI and poll for result."""
async with httpx.AsyncClient(timeout=180) as client:
# Submit task
resp = await client.get(
"https://ocr.captchaai.com/in.php",
params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
},
)
result = resp.json()
if result["status"] != 1:
raise Exception(f"Submit failed: {result['request']}")
task_id = result["request"]
# Poll for result
for _ in range(30):
await asyncio.sleep(5)
poll = await client.get(
"https://ocr.captchaai.com/res.php",
params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1",
},
)
poll_result = poll.json()
if poll_result["status"] == 1:
return poll_result["request"]
if poll_result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {poll_result['request']}")
raise Exception("Polling timeout")
async def main():
async with async_playwright() as p:
# Launch with iPhone 13 emulation
iphone = p.devices["iPhone 13"]
browser = await p.chromium.launch(headless=False)
context = await browser.new_context(**iphone)
page = await context.new_page()
await page.goto("https://example.com/mobile-form")
await page.wait_for_selector(".g-recaptcha", timeout=10000)
# Extract sitekey
sitekey = await page.get_attribute(".g-recaptcha", "data-sitekey")
pageurl = page.url
print(f"Found sitekey: {sitekey}")
# Solve via CaptchaAI
token = await solve_recaptcha(sitekey, pageurl)
print(f"Token received: {token[:50]}...")
# Inject token
await page.evaluate(f"""
document.getElementById('g-recaptcha-response').value = '{token}';
document.getElementById('g-recaptcha-response').style.display = '';
""")
# Trigger callback if it exists
await page.evaluate(f"""
try {{
const clients = ___grecaptcha_cfg.clients;
Object.keys(clients).forEach(k => {{
Object.keys(clients[k]).forEach(j => {{
if (clients[k][j] && clients[k][j].callback) {{
clients[k][j].callback('{token}');
}}
}});
}});
}} catch(e) {{}}
""")
# Submit form
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle")
print("Form submitted successfully")
await browser.close()
asyncio.run(main())
Approach 2: Selenium with Mobile Emulation (JavaScript)
Use Selenium with Chrome DevTools Protocol mobile emulation for Node.js workflows:
// selenium_mobile_captcha.js
const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
async function solveCaptcha(sitekey, pageurl) {
// Submit task
const submitResp = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: API_KEY,
method: 'userrecaptcha',
googlekey: sitekey,
pageurl: pageurl,
json: '1',
},
});
if (submitResp.data.status !== 1) {
throw new Error(`Submit failed: ${submitResp.data.request}`);
}
const taskId = submitResp.data.request;
// Poll for result
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 5000));
const pollResp = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: '1' },
});
if (pollResp.data.status === 1) return pollResp.data.request;
if (pollResp.data.request !== 'CAPCHA_NOT_READY') {
throw new Error(`Solve failed: ${pollResp.data.request}`);
}
}
throw new Error('Polling timeout');
}
async function main() {
// Configure mobile emulation
const mobileEmulation = {
deviceMetrics: { width: 390, height: 844, pixelRatio: 3.0 },
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) ' +
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
};
const options = new chrome.Options();
options.setMobileEmulation(mobileEmulation);
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
try {
await driver.get('https://example.com/mobile-form');
await driver.wait(until.elementLocated(By.css('.g-recaptcha')), 10000);
// Extract sitekey
const captchaEl = await driver.findElement(By.css('.g-recaptcha'));
const sitekey = await captchaEl.getAttribute('data-sitekey');
const pageurl = await driver.getCurrentUrl();
console.log(`Sitekey: ${sitekey}`);
// Solve CAPTCHA
const token = await solveCaptcha(sitekey, pageurl);
console.log(`Token: ${token.substring(0, 50)}...`);
// Inject token
await driver.executeScript(`
document.getElementById('g-recaptcha-response').value = arguments[0];
`, token);
// Submit form
await driver.findElement(By.css('button[type="submit"]')).click();
console.log('Form submitted');
} finally {
await driver.quit();
}
}
main().catch(console.error);
Mobile-Specific Considerations
| Factor | Desktop | Mobile | Impact on CAPTCHA |
|---|---|---|---|
| User-Agent | Desktop Chrome/Firefox | Mobile Safari/Chrome | Some sites serve different CAPTCHA configs by device |
| Viewport | 1920×1080+ | 390×844 | CAPTCHA widget may render differently |
| Touch events | Mouse events | Touch events | Some CAPTCHAs validate interaction type |
| Network | Broadband | 4G/5G | Longer timeouts may be needed |
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| CAPTCHA not rendering in mobile emulation | Site detects emulation via navigator.platform | Set platform override in DevTools Protocol |
g-recaptcha-response textarea not found |
Mobile layout uses different CAPTCHA rendering | Search for textarea by name attribute instead of ID |
| Token injection works but form still fails | Server validates User-Agent consistency | Ensure the same User-Agent is used for solving and form submission |
| Slow page loads in emulation | Resource-heavy pages | Use --disable-images Chrome flag to speed up loading |
FAQ
Should I use real mobile devices or emulation?
Emulation is faster for development and CI/CD. Real devices via Appium or ADB are better for production scenarios where fingerprinting matters.
Does CaptchaAI need to know it's a mobile CAPTCHA?
No. CaptchaAI solves based on the sitekey and page URL. Mobile vs desktop distinction doesn't affect the solving process.
Can I combine mobile emulation with a proxy?
Yes. Both Playwright and Selenium support proxy configuration alongside mobile emulation. Pass the proxy to CaptchaAI as well for consistency.
Related Articles
- How To Solve Recaptcha V2 Callback Using Api
- Zapier Captchaai No Code Automation
- Recaptcha V2 Turnstile Same Site Handling
Next Steps
Start automating mobile CAPTCHA flows — get your CaptchaAI API key and integrate with your mobile automation stack.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.