Solve reCAPTCHA Enterprise with Java

By solvethree2 min read

Use Java's HttpClient to request a token and Gson to read the JSON response. This example calls the REST API, waits for the task to finish, and returns the reCAPTCHA Enterprise solution.

On this page

Set up the site and environment

Use Java 21 or later. Create an active site profile for your target in the console, then set SOLVETHREE_API_KEY and SOLVETHREE_PROXY in the environment that launches Java.

Replace accounts.example.com with your profiled hostname and login with the action used by the page. The site field takes a hostname, without a scheme or path.

Save and run Solve.java

Java 21+ · Solve.java (Gson dependency)
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.UUID;

public class Solve {
    static final HttpClient HTTP = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(15)).build();
    static final String KEY = System.getenv("SOLVETHREE_API_KEY");

    static JsonObject api(String path, JsonObject body, long deadline) throws Exception {
        long remaining = deadline - System.nanoTime();
        if (remaining <= 0) throw new IllegalStateException("Keep the task ID and resume polling");
        var builder = HttpRequest.newBuilder(URI.create("https://api.solvethree.com/v1" + path))
            .timeout(Duration.ofNanos(Math.min(remaining, Duration.ofSeconds(30).toNanos())))
            .header("Authorization", "Bearer " + KEY)
            .header("Content-Type", "application/json");
        if (body == null) builder.GET();
        else builder.POST(HttpRequest.BodyPublishers.ofString(body.toString()));
        var response = HTTP.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300)
            throw new IllegalStateException("API HTTP " + response.statusCode());
        return JsonParser.parseString(response.body()).getAsJsonObject();
    }
    public static void main(String[] args) throws Exception {
        String proxy = System.getenv("SOLVETHREE_PROXY");
        if (KEY == null || proxy == null) throw new IllegalArgumentException("Set the API key and proxy");
        var input = new JsonObject();
        input.addProperty("site", "accounts.example.com");
        input.addProperty("action", "login");
        input.addProperty("proxy", proxy);
        input.addProperty("requestId", UUID.randomUUID().toString());
        // Save input before creation if the task must be recovered after restart.
        long deadline = System.nanoTime() + Duration.ofSeconds(180).toNanos();
        var task = api("/tasks?waitMs=0", input, deadline);
        while (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving")) {
            String id = URLEncoder.encode(task.get("id").getAsString(), StandardCharsets.UTF_8);
            task = api("/tasks/" + id + "?waitMs=15000", null, deadline);
            if (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving"))
                Thread.sleep(1000);
        }
        if (!task.get("status").getAsString().equals("ready"))
            throw new IllegalStateException("Task failed: " + task.get("errorCode")
                + ": " + task.get("error"));
        var solution = task.getAsJsonObject("solution");
        // Submit solution.token with userAgent/secChUa and the original proxy.
        System.out.println("Ready task: " + task.get("id").getAsString());
    }
}

From the directory containing Solve.java, download the Gson JAR, compile, and run:

macOS/Linux · fetch Gson and run Solve.java
curl --fail --location --output gson-2.15.0.jar \
  https://repo.maven.apache.org/maven2/com/google/code/gson/gson/2.15.0/gson-2.15.0.jar
javac -cp gson-2.15.0.jar Solve.java
java -cp .:gson-2.15.0.jar Solve

These commands use the macOS/Linux classpath separator, :. On Windows, use ; between the current directory and the Gson JAR.

Read the token from the result

Creation uses waitMs=0. The loop retrieves the same task ID while it is pending or solving, and stops at ready or failed. Each GET can wait up to 15 seconds; the caller has a 180-second overall budget.

Read the token with solution.get("token").getAsString(). Use the returned userAgent and secChUa in the target request, through the same proxy. The program prints the task ID, not the token.

Check the target response after submission. A ready task tells you a solution was generated; it does not establish that the application accepted it.

Errors and interrupted requests

The helper stops on a non-success HTTP response. A terminal failed task reports errorCode and error, and the failed solve is refunded. Use the API error reference to decide what to fix.

Save the generated requestId and exact inputs if you need recovery across restarts. If creation times out, replay those inputs with the same ID. If you already have a task ID, retrieve it. Restarting this program creates another UUID and can create another billable task.

Run your first task.

Start with $5 in workspace credit.

Create a workspace