API Tutorials

Async CAPTCHA Solving in C# with Task.WhenAll and CaptchaAI

C#'s async/await and Task.WhenAll make concurrent CAPTCHA solving straightforward. This tutorial shows how to submit multiple CAPTCHAs to CaptchaAI in parallel, poll for results concurrently, and collect all solutions — handling partial failures gracefully.

Prerequisites

dotnet new console -n CaptchaSolver
cd CaptchaSolver
dotnet add package System.Text.Json

No extra packages needed — HttpClient and Task.WhenAll are built into .NET.

Core CaptchaAI Client

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public class CaptchaAiClient : IDisposable
{
    private readonly HttpClient _client;
    private readonly string _apiKey;
    private const string SubmitUrl = "https://ocr.captchaai.com/in.php";
    private const string ResultUrl = "https://ocr.captchaai.com/res.php";

    public CaptchaAiClient(string apiKey)
    {
        _apiKey = apiKey;
        _client = new HttpClient();
    }

    public async Task<string> SolveCaptchaAsync(string sitekey, string pageurl)
    {
        // Submit
        var submitParams = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("key", _apiKey),
            new KeyValuePair<string, string>("method", "userrecaptcha"),
            new KeyValuePair<string, string>("googlekey", sitekey),
            new KeyValuePair<string, string>("pageurl", pageurl),
            new KeyValuePair<string, string>("json", "1")
        });

        var submitResp = await _client.PostAsync(SubmitUrl, submitParams);
        var submitJson = await submitResp.Content.ReadAsStringAsync();
        var submitData = JsonSerializer.Deserialize<ApiResponse>(submitJson);

        if (submitData.Status != 1)
            throw new Exception($"Submit failed: {submitData.Request}");

        var captchaId = submitData.Request;

        // Poll for result
        for (int i = 0; i < 60; i++)
        {
            await Task.Delay(5000);

            var resultResp = await _client.GetAsync(
                $"{ResultUrl}?key={_apiKey}&action=get&id={captchaId}&json=1"
            );
            var resultJson = await resultResp.Content.ReadAsStringAsync();
            var resultData = JsonSerializer.Deserialize<ApiResponse>(resultJson);

            if (resultData.Status == 1)
                return resultData.Request;

            if (resultData.Request != "CAPCHA_NOT_READY")
                throw new Exception($"Solve failed: {resultData.Request}");
        }

        throw new TimeoutException("Solve timeout after 300s");
    }

    public void Dispose() => _client.Dispose();
}

public class ApiResponse
{
    public int Status { get; set; }
    public string Request { get; set; }
}

Parallel Solving with Task.WhenAll

public class BatchSolver
{
    private readonly CaptchaAiClient _client;

    public BatchSolver(string apiKey)
    {
        _client = new CaptchaAiClient(apiKey);
    }

    public async Task<BatchResult> SolveAllAsync(
        IReadOnlyList<CaptchaTask> tasks)
    {
        var solveTasks = new Task<TaskResult>[tasks.Count];

        for (int i = 0; i < tasks.Count; i++)
        {
            var task = tasks[i];
            solveTasks[i] = SolveSingleAsync(task);
        }

        // Wait for ALL tasks — no short-circuiting on failure
        var results = await Task.WhenAll(solveTasks);

        return new BatchResult
        {
            Solved = Array.FindAll(results, r => r.Solution != null),
            Failed = Array.FindAll(results, r => r.Error != null)
        };
    }

    private async Task<TaskResult> SolveSingleAsync(CaptchaTask task)
    {
        try
        {
            var solution = await _client.SolveCaptchaAsync(
                task.Sitekey, task.Pageurl);
            return new TaskResult
            {
                TaskId = task.TaskId,
                Solution = solution
            };
        }
        catch (Exception ex)
        {
            return new TaskResult
            {
                TaskId = task.TaskId,
                Error = ex.Message
            };
        }
    }
}

public record CaptchaTask(string TaskId, string Sitekey, string Pageurl);

public class TaskResult
{
    public string TaskId { get; set; }
    public string Solution { get; set; }
    public string Error { get; set; }
}

public class BatchResult
{
    public TaskResult[] Solved { get; set; }
    public TaskResult[] Failed { get; set; }
}

Controlling Concurrency with SemaphoreSlim

public async Task<BatchResult> SolveWithLimitAsync(
    IReadOnlyList<CaptchaTask> tasks,
    int maxConcurrency = 10)
{
    var semaphore = new SemaphoreSlim(maxConcurrency);
    var solveTasks = new Task<TaskResult>[tasks.Count];

    for (int i = 0; i < tasks.Count; i++)
    {
        var task = tasks[i];
        solveTasks[i] = ThrottledSolveAsync(task, semaphore);
    }

    var results = await Task.WhenAll(solveTasks);

    return new BatchResult
    {
        Solved = Array.FindAll(results, r => r.Solution != null),
        Failed = Array.FindAll(results, r => r.Error != null)
    };
}

private async Task<TaskResult> ThrottledSolveAsync(
    CaptchaTask task, SemaphoreSlim semaphore)
{
    await semaphore.WaitAsync();
    try
    {
        return await SolveSingleAsync(task);
    }
    finally
    {
        semaphore.Release();
    }
}

Full Program Example

class Program
{
    static async Task Main(string[] args)
    {
        var apiKey = Environment.GetEnvironmentVariable("CAPTCHAAI_API_KEY")
            ?? throw new Exception("Set CAPTCHAAI_API_KEY");

        var solver = new BatchSolver(apiKey);

        // Create 20 tasks
        var tasks = new List<CaptchaTask>();
        for (int i = 0; i < 20; i++)
        {
            tasks.Add(new CaptchaTask(
                $"task_{i}",
                "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
                $"https://example.com/page/{i}"
            ));
        }

        Console.WriteLine($"Solving {tasks.Count} CAPTCHAs with concurrency=10...");
        var start = DateTime.UtcNow;

        var result = await solver.SolveWithLimitAsync(tasks, maxConcurrency: 10);

        var elapsed = DateTime.UtcNow - start;
        Console.WriteLine($"\nDone in {elapsed.TotalSeconds:F1}s");
        Console.WriteLine($"  Solved: {result.Solved.Length}");
        Console.WriteLine($"  Failed: {result.Failed.Length}");

        foreach (var s in result.Solved)
            Console.WriteLine($"  ✓ {s.TaskId}: {s.Solution[..Math.Min(30, s.Solution.Length)]}...");

        foreach (var f in result.Failed)
            Console.WriteLine($"  ✗ {f.TaskId}: {f.Error}");
    }
}

Cancellation Support

Cancel all pending tasks after a global timeout:

public async Task<BatchResult> SolveWithTimeoutAsync(
    IReadOnlyList<CaptchaTask> tasks,
    int maxConcurrency = 10,
    TimeSpan? timeout = null)
{
    using var cts = new CancellationTokenSource(
        timeout ?? TimeSpan.FromMinutes(10));

    try
    {
        return await SolveWithLimitAsync(tasks, maxConcurrency);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Batch operation timed out.");
        return new BatchResult
        {
            Solved = Array.Empty<TaskResult>(),
            Failed = Array.Empty<TaskResult>()
        };
    }
}

Task.WhenAll vs Parallel.ForEachAsync (.NET 6+)

// .NET 6+ alternative
await Parallel.ForEachAsync(tasks,
    new ParallelOptions { MaxDegreeOfParallelism = 10 },
    async (task, ct) =>
    {
        var result = await SolveSingleAsync(task);
        // Process result immediately
    });
Method Collects all results Built-in concurrency limit .NET version
Task.WhenAll + SemaphoreSlim Yes Manual (SemaphoreSlim) .NET Core 1.0+
Parallel.ForEachAsync Process inline Built-in .NET 6+

Troubleshooting

Issue Cause Fix
HttpClient socket exhaustion Creating new HttpClient per request Use single shared HttpClient (shown above)
Task.WhenAll throws on first error Not wrapping individual tasks in try/catch Catch inside SolveSingleAsync (shown above)
High memory with 1000+ tasks All tasks start immediately Use SemaphoreSlim to control concurrency
SSL/TLS errors Old .NET targeting TLS 1.0 Set ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

FAQ

Should I use a single HttpClient or one per task?

Always a single shared HttpClient. Creating a new one per request causes socket exhaustion. HttpClient is thread-safe and designed for reuse.

What's the optimal concurrency for C#?

Start with 10–20. C#'s async model is lightweight — each concurrent task uses minimal resources. Increase until CaptchaAI's capacity or your network becomes the bottleneck.

Task.WhenAll vs Task.WhenAny?

WhenAll waits for every task. WhenAny returns when the first task completes — useful for "first success wins" scenarios, but not for batch solving where you need all results.

Next Steps

Solve CAPTCHAs concurrently in C# — get your CaptchaAI API key and implement parallel solving.

Related guides:

Discussions (0)

No comments yet.

Related Posts

Tutorials Solve CAPTCHAs with C# Using CaptchaAI
C# tutorial for solving re CAPTCHA, Turnstile, and image CAPTCHAs using the Captcha AI REST API with Http Client.

C# tutorial for solving re CAPTCHA, Turnstile, and image CAPTCHAs using the Captcha AI REST API with Http Clie...

Automation All CAPTCHA Types C#
Mar 15, 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...

Automation Python All CAPTCHA Types
Apr 07, 2026
Integrations Axios + CaptchaAI: Solve CAPTCHAs Without a Browser
Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a browser.

Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a bro...

Automation All CAPTCHA Types
Apr 08, 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...

Automation Python 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...

Automation Python All CAPTCHA Types
Apr 05, 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...

Automation Python All CAPTCHA Types
Apr 07, 2026
Explainers Building Responsible Automation with CaptchaAI
Build responsible automation systems with Captcha AI.

Build responsible automation systems with Captcha AI. Guidelines for sustainable workflows, resource managemen...

Automation Python All CAPTCHA Types
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...

Automation Python All CAPTCHA Types
Apr 07, 2026
Reference CaptchaAI Rate Limits and Throttling
Understanding Captcha AI's rate limits, handling throttling errors, and implementing client-side rate control.

Understanding Captcha AI's rate limits, handling throttling errors, and implementing client-side rate control.

Automation All CAPTCHA Types Performance
Mar 21, 2026
Tutorials Webhook Endpoint Monitoring for CAPTCHA Solve Callbacks
Monitor your Captcha AI callback endpoints — track uptime, response latency, error rates, and set up alerts before missed results impact your pipeline.

Monitor your Captcha AI callback endpoints — track uptime, response latency, error rates, and set up alerts be...

Automation Python All CAPTCHA Types
Mar 12, 2026
API Tutorials Case-Sensitive CAPTCHA API Parameter Guide
How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI.

How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI. Covers when to use, comm...

Python Web Scraping Image OCR
Apr 09, 2026
API Tutorials How to Solve reCAPTCHA v2 Enterprise with Python
Solve re CAPTCHA v 2 Enterprise using Python and Captcha AI API.

Solve re CAPTCHA v 2 Enterprise using Python and Captcha AI API. Complete guide with sitekey extraction, task...

Automation Python reCAPTCHA v2
Apr 08, 2026
API Tutorials Batch Image CAPTCHA Solving: Processing 1000+ Images
Process thousands of image CAPTCHAs efficiently with Captcha AI using async queues, worker pools, and rate-aware batching in Python and Node.js.

Process thousands of image CAPTCHAs efficiently with Captcha AI using async queues, worker pools, and rate-awa...

Automation Python Image OCR
Mar 21, 2026