Solve reCAPTCHA Enterprise with C#

By solvethree2 min read

You can call solvethree directly from C#. The example below requests a reCAPTCHA v3 Enterprise token, follows the task until it finishes, and stops on a failed solve.

On this page

Before you run it

Use .NET 8+. No extra NuGet packages are needed.

  • Create an active site profile for your target hostname in the console. See site profile setup.
  • Set SOLVETHREE_API_KEY to your workspace API key and SOLVETHREE_PROXY to your HTTP(S) proxy URL.
  • Replace accounts.example.com and login with your hostname and the action used by the page. Use a hostname for site, without a scheme or path.

Create the task and wait for the result

Create a .NET console project with dotnet new console, replace Program.cs with this code, and run dotnet run.

C# · Program.cs (.NET 8+)
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;

var key = Environment.GetEnvironmentVariable("SOLVETHREE_API_KEY")
    ?? throw new Exception("Set SOLVETHREE_API_KEY");
var proxy = Environment.GetEnvironmentVariable("SOLVETHREE_PROXY")
    ?? throw new Exception("Set SOLVETHREE_PROXY");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(180));

async Task<JsonObject> Api(HttpMethod method, string path, JsonObject? body = null)
{
    using var request = new HttpRequestMessage(method, "https://api.solvethree.com/v1" + path);
    if (body is not null)
        request.Content = new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json");
    using var response = await http.SendAsync(request, deadline.Token);
    response.EnsureSuccessStatusCode();
    return JsonNode.Parse(await response.Content.ReadAsStringAsync(deadline.Token))!.AsObject();
}

var requestId = Guid.NewGuid().ToString(); // Save with these inputs for recovery.
var input = new JsonObject {
    ["site"] = "accounts.example.com", ["action"] = "login",
    ["proxy"] = proxy, ["requestId"] = requestId
};
var task = await Api(HttpMethod.Post, "/tasks?waitMs=0", input);
while (task["status"]!.GetValue<string>() is "pending" or "solving")
{
    var id = Uri.EscapeDataString(task["id"]!.GetValue<string>());
    task = await Api(HttpMethod.Get, "/tasks/" + id + "?waitMs=15000");
    if (task["status"]!.GetValue<string>() is "pending" or "solving")
        await Task.Delay(1000, deadline.Token);
}
if (task["status"]!.GetValue<string>() != "ready")
    throw new Exception("Task failed: " + task["errorCode"]?.GetValue<string>()
        + ": " + task["error"]?.GetValue<string>());
var solution = task["solution"]!.AsObject();
// Use solution["token"], ["userAgent"] and ["secChUa"] with the original proxy.
Console.WriteLine("Ready task: " + task["id"]!.GetValue<string>());

The POST uses waitMs=0 to return a task immediately. Each GET then waits up to 15 seconds for an update. The loop continues for pending or solving, and stops at ready or failed.

The request defaults to Enterprise v3. For standard v3, add recaptchaType: v3. Keep type and url out of a reCAPTCHA task body.

Use the token in your application

The token is solution["token"]!.GetValue<string>(). The task ID printed by the program is for retrieving the task; use the token value in your application's CAPTCHA field.

Read solution["userAgent"] and solution["secChUa"] for the target request's User-Agent and sec-ch-ua headers.

Send that request through the same proxy used for the solve. Then check the application response: a generated token does not establish that the target accepted the request.

If the request fails

A failed task includes errorCode and error; the example stops and reports them. Failed solves are refunded. An HTTP error such as 401 or 402 is a separate request problem—check the API error reference before retrying.

Keep the requestId and exact task inputs if you need recovery after a timeout or process restart. If you have the task ID, poll it. Otherwise, resend the original inputs with the same requestId. Simply restarting the program generates another ID and can create another billable solve.

The caller stops waiting after its deadline; that does not cancel a task already created. See polling and timeout recovery for the full sequence.

Run your first task.

Start with $5 in workspace credit.

Create a workspace