Sequential solving is simple. Parallel solving is fast. The right choice depends on your volume, infrastructure, and tolerance for complexity. This guide compares both approaches with CaptchaAI and helps you pick the right strategy.
Head-to-Head Comparison
| Factor | Sequential | Parallel |
|---|---|---|
| Throughput (reCAPTCHA v2, 15s median) | ~240/hour | ~10,000+/hour |
| Code complexity | Simple | Moderate to complex |
| Error handling | Straightforward | Requires concurrent error isolation |
| Memory usage | Minimal (~30 MB) | Scales with concurrency (~100–500 MB) |
| API cost per solve | Same | Same |
| Debugging difficulty | Easy | Harder (race conditions, timing) |
| Order preservation | Guaranteed | Requires explicit tracking |
| Best for | < 500 solves/day | > 500 solves/day |
Sequential Solving
How It Works
One CAPTCHA at a time: submit → wait → poll → get result → next.
# sequential_solver.py
import os
import time
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
def solve_sequential(tasks):
"""Solve CAPTCHAs one by one."""
results = []
session = requests.Session()
for task in tasks:
# Submit
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": task["sitekey"],
"pageurl": task["pageurl"],
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
results.append({"error": result.get("request")})
continue
task_id = result["request"]
time.sleep(15)
# Poll
token = None
for _ in range(25):
poll = session.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.get("status") == 1:
token = poll_result["request"]
break
if poll_result.get("request") != "CAPCHA_NOT_READY":
break
time.sleep(5)
results.append({"token": token} if token else {"error": "timeout"})
return results
# 10 tasks sequentially → ~150 seconds total
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = solve_sequential(tasks)
print(f"Completed in {time.time() - start:.0f}s")
When Sequential Makes Sense
- Single-page workflows — Filling out one form at a time
- Debugging and development — Clear execution flow
- Low volume — < 500 solves/day doesn't justify parallel complexity
- Order-dependent tasks — When solve results must be used in sequence
Parallel Solving
How It Works
Submit all CAPTCHAs at once, poll for results concurrently:
# parallel_solver.py
import os
import asyncio
import aiohttp
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
async def solve_one(session, sitekey, pageurl, semaphore):
"""Solve a single CAPTCHA within concurrency limits."""
async with semaphore:
# Submit
async with session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": pageurl, "json": "1",
}) as resp:
result = await resp.json(content_type=None)
if result.get("status") != 1:
return {"error": result.get("request")}
task_id = result["request"]
await asyncio.sleep(15)
# Poll
for _ in range(25):
async with session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
}) as resp:
poll_result = await resp.json(content_type=None)
if poll_result.get("status") == 1:
return {"token": poll_result["request"]}
if poll_result.get("request") != "CAPCHA_NOT_READY":
return {"error": poll_result.get("request")}
await asyncio.sleep(5)
return {"error": "timeout"}
async def solve_parallel(tasks, max_concurrent=50):
"""Solve CAPTCHAs in parallel with concurrency control."""
semaphore = asyncio.Semaphore(max_concurrent)
connector = aiohttp.TCPConnector(limit=max_concurrent)
async with aiohttp.ClientSession(connector=connector) as session:
coros = [
solve_one(session, t["sitekey"], t["pageurl"], semaphore)
for t in tasks
]
return await asyncio.gather(*coros)
# 10 tasks in parallel → ~20 seconds total
import time
tasks = [{"sitekey": "SITEKEY", "pageurl": "https://example.com"}] * 10
start = time.time()
results = asyncio.run(solve_parallel(tasks))
print(f"Completed in {time.time() - start:.0f}s")
JavaScript Parallel Example
// parallel_solver.js
const axios = require('axios');
const https = require('https');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const agent = new https.Agent({ keepAlive: true, maxSockets: 50 });
const api = axios.create({ baseURL: 'https://ocr.captchaai.com', httpsAgent: agent });
async function solveOne(sitekey, pageurl) {
const submit = await api.get('/in.php', {
params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: '1' },
});
if (submit.data.status !== 1) return { error: submit.data.request };
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await api.get('/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) return { token: poll.data.request };
if (poll.data.request !== 'CAPCHA_NOT_READY') return { error: poll.data.request };
await new Promise(r => setTimeout(r, 5000));
}
return { error: 'timeout' };
}
(async () => {
const tasks = Array.from({ length: 10 }, () => ({
sitekey: 'SITEKEY', pageurl: 'https://example.com',
}));
const start = Date.now();
const results = await Promise.all(tasks.map(t => solveOne(t.sitekey, t.pageurl)));
console.log(`Completed in ${((Date.now() - start) / 1000).toFixed(0)}s`);
console.log(`Solved: ${results.filter(r => r.token).length}/${tasks.length}`);
agent.destroy();
})();
Throughput by Concurrency Level
| Concurrent solves | Estimated throughput/hour | Memory (Python) | Complexity |
|---|---|---|---|
| 1 (sequential) | 240 | 30 MB | Low |
| 10 | 2,400 | 50 MB | Low |
| 25 | 6,000 | 80 MB | Medium |
| 50 | 10,000+ | 120 MB | Medium |
| 100 | 18,000+ | 200 MB | High |
Based on reCAPTCHA v2 with 15s median solve time.
Hybrid Approach
Use sequential for the workflow and parallel for the CAPTCHA solving:
# Process 10 URLs sequentially, but solve their CAPTCHAs in a parallel batch
urls = get_next_batch() # 10 URLs
captcha_params = [extract_sitekey(url) for url in urls] # Sequential extraction
tokens = asyncio.run(solve_parallel(captcha_params, max_concurrent=10)) # Parallel solving
for url, result in zip(urls, tokens):
submit_form(url, result.get("token")) # Sequential submission
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Parallel slower than expected | Semaphore too restrictive | Increase max_concurrent |
| Random failures in parallel | Shared state corruption | Isolate state per coroutine/promise |
| Results out of order | asyncio.gather preserves order |
Use index tracking if needed |
API returns ERROR_NO_SLOT_AVAILABLE |
Too many concurrent submissions | Add small delay between submissions |
FAQ
Can I switch from sequential to parallel without changing my code structure?
Yes, if you extract the solve logic into a standalone function. The only change is wrapping calls in asyncio.gather (Python) or Promise.all (JavaScript).
Does parallel solving cost more?
No. CaptchaAI charges per solve, not per concurrent request. Parallel solving has the same API cost as sequential — you just reach the total faster.
What's the maximum practical concurrency?
Most setups hit diminishing returns above 100 concurrent solves. Beyond that, focus on optimizing solve parameters rather than adding concurrency.
Next Steps
Pick the strategy that matches your volume — get your CaptchaAI API key.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.