Call solvethree from an ASP.NET Core backend
Register a typed HttpClient, create the task once, then retrieve its status with a finite deadline. Keep the API key on the server and protect any application routes that call the client.
The example below is a REST helper for reCAPTCHA Enterprise, not an official C# SDK. It keeps task creation and retrieval separate so your application doesn't need to hold one incoming request open until solving finishes.
On this page
Register the typed client
First, activate the target site's profile for your workspace in the console. Sending a hostname and action won't create one. See site profile setup if the site isn't configured yet.
Put the workspace key in server configuration as SOLVETHREE_API_KEY. Add the client class, then uncomment the registration shown under Program.cs and place it before builder.Build().
// CaptchaClient.cs; .NET 8+, System.Net.Http.Json and System.Text.Json.Nodes.
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
public sealed class CaptchaClient(HttpClient http)
{
public async Task<JsonObject> CreateAsync(string site, string proxy, string requestId,
CancellationToken cancellationToken)
{
using var response = await http.PostAsJsonAsync("tasks?waitMs=0",
new { site, action = "login", proxy, requestId }, cancellationToken);
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken))!;
}
public async Task<JsonObject> GetAsync(string id, CancellationToken cancellationToken)
{
using var response = await http.GetAsync("tasks/" + Uri.EscapeDataString(id) + "?waitMs=15000",
cancellationToken);
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync<JsonObject>(cancellationToken))!;
}
}
// Program.cs, before builder.Build(), in an existing authenticated ASP.NET app:
// builder.Services.AddHttpClient<CaptchaClient>(http => {
// http.BaseAddress = new Uri("https://api.solvethree.com/v1/");
// http.Timeout = TimeSpan.FromSeconds(25);
// http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
// "Bearer", builder.Configuration["SOLVETHREE_API_KEY"]
// ?? throw new InvalidOperationException("Missing API key"));
// }).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler {
// AllowAutoRedirect = false
// });
// Inject CaptchaClient into your authenticated controller/service. Pass its
// cancellation token, create once, save id and poll GetAsync with a finite deadline.The registration sets the API base address, Bearer authentication and a 25-second transport timeout, and disables automatic redirects. Keep the trailing slash on the base address: the client's relative tasks paths must resolve under /v1/.
The helper uses HttpClient's JSON extensions and returns a JsonObject. Both methods check HTTP success before deserializing. Inject CaptchaClient into an authenticated controller or service; the helper itself doesn't authenticate or authorize your application's callers.
Create once and save the recovery information
- 1.
Choose the site, action and proxy
Pass the target hostname as site, not a full URL. This sample fixes action to login; change it to the action your target expects. It sends one proxy and relies on the default enterprise-v3 reCAPTCHA mode. Don't add type or url fields.
- 2.
Save a requestId before calling CreateAsync
Use 1–64 ASCII letters, numbers, underscores or hyphens. Save it with the exact task inputs so you can recover the same logical task if the response is lost.
- 3.
Call CreateAsync and save the returned task ID
CreateAsync makes one POST with waitMs=0. Pass the caller's CancellationToken. Once you have the task ID, use GetAsync to retrieve that task rather than creating another one.
Keep permission to spend workspace credits behind your application's authorization checks. If these values come from an incoming request, validate the requested site, action and proxy against what that caller is allowed to use.
Poll with an overall deadline
GetAsync escapes the known task ID and requests waitMs=15000. The 25-second HttpClient timeout limits each HTTP request; it does not limit the total time your application spends polling. Set a separate overall deadline and pass cancellation through every call.
pendingorsolving: continue only while the polling deadline and caller cancellation allow it.ready: stop polling and read the solution. This is the only status with a usable solution.failed: stop polling and inspecterrorCodeanderror.
For a browser-facing application, keep creation and later status checks as separate, protected operations rather than looping inside one incoming request until the task finishes. Retain the task ID when a local deadline expires so a later authorized request can retrieve it.
When submitting the token to its intended target, use the solution's returned userAgent and secChUa identity and the same proxy used for solving. A ready task means a solution was generated—not that your application request succeeded.
Recover a timeout without creating duplicate work
- CreateAsync times out or is cancelled before you receive an ID.
The server may already have created the task. Local cancellation doesn't cancel remote work.
Try this: Recover by resubmitting the same task inputs with the saved requestId. The same ID and inputs return the existing task; changing the inputs while reusing that ID conflicts.
- GetAsync times out or the polling deadline expires.
You already have a task ID, so a missing response doesn't require a replacement task.
Try this: Keep the ID and retrieve that task later within a new bounded operation.
- The HTTP call returns a non-success response.
EnsureSuccessStatusCode throws before the helper reads a success payload. This is different from receiving a task whose status is failed.
Try this: Handle the HTTP error separately. Correct invalid inputs rather than retrying them unchanged.
- The task reaches failed.
Read errorCode and error for the task failure, such as site-unreachable.
Try this: Stop polling that task and address the reported cause before starting another solve.
Use the API error guide when you need to distinguish a rejected API request from a terminal solve failure.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.