API Tutorials

Python asyncio + CaptchaAI: Concurrent Solving at Scale

Sequential CAPTCHA solving caps throughput at one solve per cycle — around 20–30 seconds each. With Python asyncio and aiohttp, you can solve dozens of CAPTCHAs concurrently while keeping resource usage flat.


Prerequisites

pip install aiohttp

Basic async solver

import asyncio
import aiohttp

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
API_KEY = "YOUR_API_KEY"


async def solve_captcha(session, method, params):
    """Submit a CAPTCHA task and poll for the result."""
    data = {
        "key": API_KEY,
        "method": method,
        "json": 1,
        **params
    }

    # Submit
    async with session.post(SUBMIT_URL, data=data) as resp:
        result = await resp.json(content_type=None)

    if result.get("status") != 1:
        raise Exception(f"Submit error: {result.get('error_text', result.get('request'))}")

    task_id = result["request"]

    # Poll
    for _ in range(24):  # 24 * 5s = 120s max
        await asyncio.sleep(5)
        async with session.get(RESULT_URL, params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1
        }) as resp:
            result = await resp.json(content_type=None)

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Poll error: {result.get('error_text', result.get('request'))}")

    raise Exception(f"Timeout: task {task_id}")


async def main():
    async with aiohttp.ClientSession() as session:
        token = await solve_captcha(session, "userrecaptcha", {
            "googlekey": "6Le-SITEKEY",
            "pageurl": "https://example.com"
        })
        print(f"Token: {token[:50]}...")


asyncio.run(main())

Expected output:

Token: 03AGdBq26ZfPxL...

Concurrent solving with semaphore

Use asyncio.Semaphore to limit concurrency and prevent rate limit errors:

import asyncio
import aiohttp

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
API_KEY = "YOUR_API_KEY"

MAX_CONCURRENT = 10


async def solve_captcha(session, semaphore, method, params, label=""):
    async with semaphore:
        data = {
            "key": API_KEY,
            "method": method,
            "json": 1,
            **params
        }

        async with session.post(SUBMIT_URL, data=data) as resp:
            result = await resp.json(content_type=None)

        if result.get("status") != 1:
            error = result.get("error_text", result.get("request"))
            print(f"[{label}] Submit failed: {error}")
            return None

        task_id = result["request"]
        print(f"[{label}] Submitted: {task_id}")

        for _ in range(24):
            await asyncio.sleep(5)
            async with session.get(RESULT_URL, params={
                "key": API_KEY,
                "action": "get",
                "id": task_id,
                "json": 1
            }) as resp:
                result = await resp.json(content_type=None)

            if result.get("status") == 1:
                print(f"[{label}] Solved: {result['request'][:40]}...")
                return result["request"]
            if result.get("request") != "CAPCHA_NOT_READY":
                print(f"[{label}] Error: {result.get('error_text', result.get('request'))}")
                return None

        print(f"[{label}] Timeout")
        return None


async def main():
    semaphore = asyncio.Semaphore(MAX_CONCURRENT)

    # 20 tasks — only 10 run concurrently
    tasks_params = [
        {"googlekey": f"6Le-SITEKEY-{i}", "pageurl": f"https://example.com/page/{i}"}
        for i in range(20)
    ]

    async with aiohttp.ClientSession() as session:
        tasks = [
            solve_captcha(session, semaphore, "userrecaptcha", params, label=f"task-{i}")
            for i, params in enumerate(tasks_params)
        ]

        results = await asyncio.gather(*tasks, return_exceptions=True)

    solved = [r for r in results if r and not isinstance(r, Exception)]
    failed = len(results) - len(solved)
    print(f"\nResults: {len(solved)} solved, {failed} failed")


asyncio.run(main())

Expected output:

[task-0] Submitted: 72345678901
[task-1] Submitted: 72345678902
...
[task-0] Solved: 03AGdBq26ZfPxL...
[task-1] Solved: 03AGdBq27AbCdE...
...
Results: 18 solved, 2 failed

Mixed CAPTCHA types

Solve different CAPTCHA types in the same batch:

async def solve_mixed_batch(session, semaphore):
    tasks = [
        solve_captcha(session, semaphore, "userrecaptcha", {
            "googlekey": "6Le-SITEKEY-A",
            "pageurl": "https://site-a.com"
        }, label="recaptcha-1"),

        solve_captcha(session, semaphore, "turnstile", {
            "sitekey": "0x4AAAA-SITEKEY-B",
            "pageurl": "https://site-b.com"
        }, label="turnstile-1"),

        solve_captcha(session, semaphore, "userrecaptcha", {
            "googlekey": "6Le-SITEKEY-C",
            "pageurl": "https://site-c.com"
        }, label="recaptcha-2"),
    ]

    return await asyncio.gather(*tasks, return_exceptions=True)

Error handling patterns

Retry with backoff

async def solve_with_retry(session, semaphore, method, params, max_retries=2):
    for attempt in range(max_retries + 1):
        try:
            return await solve_captcha(session, semaphore, method, params,
                                        label=f"attempt-{attempt}")
        except Exception as e:
            if "ERROR_ZERO_BALANCE" in str(e):
                raise  # Don't retry permanent errors
            if attempt < max_retries:
                wait = 2 ** attempt
                print(f"Retry in {wait}s: {e}")
                await asyncio.sleep(wait)
            else:
                raise

Collect results with error tracking

async def solve_batch_with_tracking(session, semaphore, task_list):
    results = {"solved": [], "failed": [], "errors": []}

    async def tracked_solve(task_params, index):
        try:
            token = await solve_captcha(session, semaphore,
                                         task_params["method"],
                                         task_params["params"],
                                         label=f"task-{index}")
            if token:
                results["solved"].append({"index": index, "token": token})
            else:
                results["failed"].append(index)
        except Exception as e:
            results["errors"].append({"index": index, "error": str(e)})

    tasks = [tracked_solve(t, i) for i, t in enumerate(task_list)]
    await asyncio.gather(*tasks)

    return results

Performance tuning

Parameter Default Recommended Notes
MAX_CONCURRENT 10 5–20 Start low, increase based on error rate
Poll interval 5s 5s Don't go below 3s
Max poll attempts 24 24 (120s) Increase for complex types
Connection pool aiohttp default 100 aiohttp.TCPConnector(limit=100)
Timeout per request None 15s aiohttp.ClientTimeout(total=15)

Optimized connector

connector = aiohttp.TCPConnector(limit=100, keepalive_timeout=30)
timeout = aiohttp.ClientTimeout(total=15)

async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
    # Use session for all solves
    pass

Troubleshooting

Problem Cause Fix
ERROR_NO_SLOT_AVAILABLE Too many concurrent submits Lower MAX_CONCURRENT
ClientConnectorError DNS or network issue Check connectivity, add retry
All tasks timeout API under load Increase poll timeout to 180s
RuntimeError: Event loop closed Wrong asyncio usage on Windows Use asyncio.run() or WindowsSelectorEventLoopPolicy

FAQ

How many concurrent tasks should I run?

Start with 10. Monitor for ERROR_NO_SLOT_AVAILABLE and adjust. Most accounts support 20+ concurrent tasks.

Does asyncio use multiple CPU cores?

No. asyncio is single-threaded — it handles I/O concurrency, not CPU parallelism. For CAPTCHA solving, the bottleneck is network I/O, so asyncio is ideal.

Can I cancel in-progress tasks?

Yes. Use task.cancel() on any asyncio task. The pending API requests will be abandoned, but CaptchaAI will still process them (you'll be charged).


Start solving CAPTCHAs concurrently with CaptchaAI

Get your API key at captchaai.com.


Full Working Code

Complete runnable examples for this article in Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin & Bash.

View on GitHub →

Discussions (0)

No comments yet.