Scarnox · Blog

Solveur de captcha en C# avec HttpClient : exemple .NET (2026)

En C#, un solveur de captcha est une petite classe autour de HttpClient. Vous envoyez la sitekey et l'URL de la page à l'API Scarnox, lisez le token dans la réponse JSON, puis le soumettez au formulaire du site. Ce guide construit cette classe pour .NET 8, pour PopularCaptcha, que Scarnox résout aujourd'hui sans proxy.

Démarrez avec un crédit d'essai gratuit, sans carte bancaire et testez le code sur une page que vous contrôlez avant de l'utiliser ailleurs.

Prérequis

Créer le projet

dotnet new console -n CaptchaDemo
cd CaptchaDemo

Aucun paquet supplémentaire n'est nécessaire. System.Net.Http et System.Text.Json font partie du framework.

La classe du solveur

La classe ci-dessous possède un HttpClient avec l'en-tête Bearer et un délai d'attente de 185 secondes. Elle renvoie le token ou lève une exception avec le texte d'erreur de l'API :

// ScarnoxSolver.cs
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;

public sealed class ScarnoxSolver : IDisposable
{
    private const string ApiUrl = "https://api.scarnox.com/api/tasks/create";
    private readonly HttpClient _http;

    public ScarnoxSolver(string apiKey)
    {
        _http = new HttpClient { Timeout = TimeSpan.FromSeconds(185) };
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
    }

    public async Task<string> SolveAsync(
        string sitekey,
        string pageUrl,
        bool invisible = false,
        CancellationToken ct = default)
    {
        var body = new Dictionary<string, object>
        {
            ["captcha_type"] = "PopularCaptchaTaskProxyless",
            ["site_key"] = sitekey,
            ["site_url"] = pageUrl,
        };
        if (invisible)
        {
            body["is_invisible"] = true;
        }

        // L'appel revient lorsque le token est prêt. Il n'y a pas de polling.
        using var resp = await _http.PostAsJsonAsync(ApiUrl, body, ct);
        var data = await resp.Content.ReadFromJsonAsync<CreateTaskResponse>(cancellationToken: ct)
                   ?? throw new InvalidOperationException(
                       $"HTTP {(int)resp.StatusCode}: empty response");

        if (data.ErrorId != 0)
        {
            throw new InvalidOperationException($"{data.ErrorCode}: {data.ErrorDescription}");
        }

        return data.Solution?.Token
               ?? throw new InvalidOperationException("Response had no token");
    }

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

public sealed class CreateTaskResponse
{
    [JsonPropertyName("errorId")]
    public int ErrorId { get; set; }

    [JsonPropertyName("errorCode")]
    public string? ErrorCode { get; set; }

    [JsonPropertyName("errorDescription")]
    public string? ErrorDescription { get; set; }

    [JsonPropertyName("solution")]
    public Solution? Solution { get; set; }
}

public sealed class Solution
{
    [JsonPropertyName("token")]
    public string? Token { get; set; }
}

La vérification de errorId est la ligne importante. L'API renvoie 0 en cas de succès. Toute autre valeur s'accompagne de errorCode et errorDescription, et la classe remonte les deux.

Soumettre le token

Le token part vers le formulaire du site. Utilisez un second HttpClient avec un conteneur de cookies, pour que la session qui a chargé le formulaire soit réutilisée lors du POST :

// Program.cs
using System.Net;

var apiKey = Environment.GetEnvironmentVariable("SCARNOX_API_KEY")
             ?? throw new InvalidOperationException("Set SCARNOX_API_KEY");

const string pageUrl = "https://example.com/signup";
const string sitekey = "YOUR-SITEKEY-UUID";

using var solver = new ScarnoxSolver(apiKey);
string token = "";

// Relancer les échecs transitoires. Les résolutions échouées ne sont pas facturées.
for (int attempt = 1; attempt <= 3; attempt++)
{
    try
    {
        token = await solver.SolveAsync(sitekey, pageUrl);
        break;
    }
    catch (InvalidOperationException ex) when (attempt < 3)
    {
        Console.Error.WriteLine($"Attempt {attempt} failed: {ex.Message}");
        await Task.Delay(TimeSpan.FromSeconds(attempt));
    }
}

var cookies = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookies, UseCookies = true };
using var site = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(60) };

// GET d'abord pour que le site définisse son cookie de session.
await site.GetAsync(pageUrl);

using var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["email"] = "you@example.com",
    ["h-captcha-response"] = token,
    ["g-recaptcha-response"] = token,
});

using var response = await site.PostAsync(pageUrl, form);
Console.WriteLine($"Status: {(int)response.StatusCode}");

Exécutez-le :

set SCARNOX_API_KEY=scarnox_YOURAPIKEY
dotnet run

Sous PowerShell, utilisez plutôt $env:SCARNOX_API_KEY = "scarnox_YOURAPIKEY".

Pourquoi cette structure

**Un seul HttpClient par solveur.** HttpClient est conçu pour être réutilisé, et non créé à chaque appel. Le solveur en possède un et le libère à la fin.

Un délai d'attente de 185 secondes. L'API bloque jusqu'à ce que le token soit prêt. Un délai plus court annule des résolutions sur le point de se terminer.

Désérialisation typée. ReadFromJsonAsync associe le JSON à CreateTaskResponse, de sorte que les vérifications d'erreur et de token se lisent comme du C# ordinaire.

La soumission du formulaire. Le site reçoit à la fois h-captcha-response et g-recaptcha-response, car les sites lisent l'un ou l'autre. Le conteneur de cookies conserve la session entre le GET et le POST.

Timing

Le token expire en environ deux minutes. Résolvez juste avant de soumettre. Si vous résolvez tôt dans un lot et soumettez bien plus tard, le token sera périmé. L'expiration des tokens de captcha détaille le sujet.

Widgets invisibles

Passez invisible: true à SolveAsync lorsque le widget est invisible. La requête inclut alors is_invisible: true. Certaines pages ont besoin d'un callback JavaScript qu'un client côté serveur ne peut pas déclencher. Dans ce cas, un outil d'automatisation de navigateur est un meilleur choix. Le guide sur le captcha invisible présente les schémas à suivre.

Scraper de manière responsable

Si le programme scrape des pages publiques, consultez d'abord le robots.txt et les conditions du site. Appliquez un délai entre les requêtes, et ne conservez que les données personnelles dont vous avez besoin. Pour le cadre juridique, consultez le web scraping est-il légal.

Tarifs

Scarnox facture par résolution réussie, et les résolutions échouées sont gratuites. Consultez les tarifs actuels pour les montants. Démarrez avec un crédit d'essai gratuit, sans carte bancaire.

Étapes suivantes