Scarnox · Blog

Solver captcha in C# con HttpClient: esempio .NET (2026)

In C#, un solver captcha è una piccola classe attorno a HttpClient. Invii la sitekey e l'URL della pagina all'API di Scarnox, leggi il token dalla risposta JSON e lo invii al form del sito. Questa guida costruisce quella classe per .NET 8, per PopularCaptcha, che Scarnox risolve oggi in modalità proxyless.

Inizia con crediti di prova gratuiti, senza carta di credito e prova il codice su una pagina che controlli prima di usarlo altrove.

Requisiti

Crea il progetto

dotnet new console -n CaptchaDemo
cd CaptchaDemo

Non servono pacchetti aggiuntivi. System.Net.Http e System.Text.Json fanno parte del framework.

La classe solver

La classe seguente possiede un HttpClient con l'header Bearer e un timeout di 185 secondi. Restituisce il token oppure lancia un'eccezione con il testo di errore dell'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;
        }

        // La chiamata ritorna quando il token è pronto. Non c'è 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; }
}

Il controllo di errorId è la riga più importante. L'API restituisce 0 in caso di successo. Qualsiasi altro valore arriva con errorCode e errorDescription, e la classe li espone entrambi.

Invia il token

Il token va al form del sito. Usa un secondo HttpClient con un cookie container, così la sessione che ha caricato il form viene riusata nella 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 = "";

// Riprova gli errori temporanei. Le risoluzioni fallite non vengono addebitate.
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) };

// Prima una GET, così il sito imposta il cookie di sessione.
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}");

Eseguilo:

set SCARNOX_API_KEY=scarnox_YOURAPIKEY
dotnet run

In PowerShell usa invece $env:SCARNOX_API_KEY = "scarnox_YOURAPIKEY".

Perché è strutturata così

**Un HttpClient per solver.** HttpClient è pensato per essere riutilizzato, non creato a ogni chiamata. Il solver ne possiede uno e lo rilascia alla fine.

Un timeout di 185 secondi. L'API si blocca finché il token è pronto. Un timeout più corto annulla risoluzioni che stavano per finire.

Deserializzazione tipizzata. ReadFromJsonAsync mappa il JSON su CreateTaskResponse, quindi i controlli su errore e token si leggono come normale C#.

L'invio del form. Il sito riceve sia h-captcha-response sia g-recaptcha-response, perché i siti leggono l'uno o l'altro. Il cookie container mantiene la sessione tra la GET e la POST.

Tempistiche

Il token scade in circa due minuti. Risolvi subito prima di inviare. Se risolvi in anticipo in un batch e invii molto più tardi, il token sarà scaduto. Scadenza del token captcha spiega i dettagli.

Widget invisibili

Passa invisible: true a SolveAsync quando il widget è invisibile. La richiesta include allora is_invisible: true. Alcune pagine richiedono una callback JavaScript che un client lato server non può attivare. In quel caso, uno strumento di automazione del browser è la scelta migliore. La guida sul captcha invisibile tratta i pattern.

Scraping responsabile

Se il programma fa scraping di pagine pubbliche, controlla prima robots.txt e i termini del sito. Usa un ritardo tra le richieste e conserva solo i dati personali di cui hai bisogno. Per il quadro legale, vedi il web scraping è legale.

Prezzi

Scarnox addebita per ogni risoluzione riuscita, e le risoluzioni fallite sono gratuite. Consulta i prezzi attuali per le tariffe. Inizia con crediti di prova gratuiti, senza carta di credito.

Prossimi passi