Call the CAPTCHA API from Spring Boot
Register one server-side CaptchaClient bean, create a task once, then poll its saved ID. The helper below uses synchronous Spring RestClient calls and returns Gson JsonObject responses.
On this page
Add the dependencies and register the helper
The supplied helper uses Java 21+, Spring Web 7.0.10, and Gson 2.15.0. In an existing Spring Boot project, keep Boot’s managed compatible Spring version rather than overriding only spring-web to match the example. Add Gson as a dependency if your application doesn’t already include it.
// Java 21+, Spring Web 7.0.10 and Gson 2.15.0; use your Boot dependency management.
// Save as CaptchaClient.java in your application's package.
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.http.HttpClient;
import java.time.Duration;
import org.springframework.http.MediaType;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
public final class CaptchaClient {
private final RestClient api;
public CaptchaClient(String key) {
var transport = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NEVER).build();
var factory = new JdkClientHttpRequestFactory(transport);
factory.setReadTimeout(Duration.ofSeconds(25));
api = RestClient.builder().baseUrl("https://api.solvethree.com/v1")
.requestFactory(factory).defaultHeader("Authorization", "Bearer " + key).build();
}
public JsonObject create(String site, String proxy, String requestId) {
var input = new JsonObject();
input.addProperty("site", site);
input.addProperty("action", "login");
input.addProperty("proxy", proxy);
input.addProperty("requestId", requestId); // Save before calling create.
String body = api.post().uri("/tasks?waitMs=0").contentType(MediaType.APPLICATION_JSON)
.body(input.toString()).retrieve().body(String.class);
return JsonParser.parseString(body).getAsJsonObject();
}
public JsonObject get(String id) {
if (!id.matches("[A-Za-z0-9_-]+")) throw new IllegalArgumentException("Invalid task ID");
String body = api.get().uri("/tasks/{id}?waitMs=15000", id)
.retrieve().body(String.class);
return JsonParser.parseString(body).getAsJsonObject();
}
}
// Construct once with System.getenv("SOLVETHREE_API_KEY") in a server-side @Bean.
// Call create once, save its id, then get(id) until ready/failed or your deadline.
// RestClient throws on HTTP errors; keep the saved requestId and any known task id.Save the class as CaptchaClient.java in your application’s package. In a server-side @Bean factory method, construct it once using the SOLVETHREE_API_KEY environment variable. Check that the key is configured at startup. The helper adds it as a Bearer authorization header; keep it out of browser code.
Reuse that bean through constructor injection. RestClient is reusable across threads, but each request blocks its calling thread. Don’t invoke these methods on a WebFlux event loop. Spring’s REST client documentation describes the synchronous client.
The transport uses a 10-second connect timeout, a 25-second read timeout, and no redirects. Responses are read as strings and parsed with Gson, not mapped to typed response objects.
Set the site, action, and proxy
Before calling create, configure an active profile for the target site in your workspace. Sending a hostname and action doesn’t create that profile. See site profile setup if the site isn’t configured yet.
- Pass the hostname as
site, not a full page URL. A reCAPTCHA request has nourlortypefield. - The helper hardcodes
actiontologin. Change that value, or make it a method parameter, if the intended request uses a different action. - Pass one proxy URL. The API requires exactly one of
proxyorproxyCandidates; this helper implements the single-proxy option. - The helper omits
recaptchaType, so the API uses itsenterprise-v3default.
Create once, then poll the saved task ID
- 1.
Save a request ID before creation
Choose a
requestIdcontaining 1–64 ASCII letters, numbers, underscores, or hyphens. Save it with the task inputs before callingcreate. It lets you recover the same task if the create response is lost. - 2.
Call create and save the returned id
Call the bean’s
createmethod with the hostname, proxy, and saved request ID. It sends one POST to/v1/taskswithwaitMs=0. Save the returnedid; use that task ID, not the request ID, for subsequent lookups. - 3.
Call get until the task reaches a terminal status
Pass the saved task ID to
get. Each lookup requestswaitMs=15000and blocks while waiting for the response. Continue forpendingorsolving, subject to an application-level deadline. Stop forreadyorfailed. The HTTP timeouts limit individual requests, not the whole polling loop. - 4.
Read the solution only when ready
For reCAPTCHA, submit
solution.tokento the intended target—not the task ID. Use the returneduserAgentandsecChUawith the same successful proxy. Don’t substitute unrelated browser headers or change the target URL. A ready task supplies a solution; it doesn’t establish that the target accepted your subsequent request.
Handle HTTP errors separately from failed tasks
- The create call times out before you receive a task ID.
The server may have created the task even though your client didn’t receive its response.
Try this: Recover by repeating the same task request with the saved
requestId. Matching inputs return the existing task. Don’t generate a new request ID unless you intend to create a new logical task.- A get call fails at the HTTP layer.
RestClient.retrieve()throws on non-success HTTP responses. That is not a task status offailed.Try this: Handle the HTTP error before attempting to read a success payload. Keep the known task ID for recovery rather than creating another task. Diagnose invalid inputs before retrying them unchanged.
- The task response has status
failed. Read its
errorCodeanderror;site-unreachableis one possible task error code.Try this: Stop polling that task and address the reported cause before starting another. Failed solves are refunded; don’t assume the failure means insufficient credit.
- Reusing a request ID produces a conflict.
Compare the new task inputs with those saved for that request ID.
Try this: Use the original inputs to recover the existing task. Use a new request ID for intentionally different task inputs.
For error interpretation, see CAPTCHA API errors.
Sources and API reference
Run your first task.
Start with $5 in workspace credit.