Quickstart
- Create an account and copy your API key from the dashboard.
- Activate a plan. A key cannot solve anything without an active plan. Paid plans: checkout is coming soon.
- Send your first request. The base URL is
https://api.solvecha.com.
curl -X POST "https://api.solvecha.com/v1/recognition/textcaptcha" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image_data": ["<BASE64 IMAGE>"]}'
# {"data": "1234567"}The answer to a request is not in the first response. You get a job id. Fetch the answer with a second request, see Polling pattern.
Authentication
Send your key in one of these ways. The key is the same everywhere.
| Method | Where |
|---|---|
key field | JSON or form body of a POST request |
Authorization: Bearer <key> | Header, any request |
Authorization: Basic <key> | Header, any request. The raw key is accepted. |
?key=<key> | Query string, for GET requests |
Keep your key secret. Send requests from your server, not from a browser page.
Endpoints
| Method and path | Purpose |
|---|---|
POST https://api.solvecha.com/v1/recognition/recaptcha | Create a reCAPTCHA grid job |
POST https://api.solvecha.com/v1/recognition/textcaptcha | Create a text captcha job |
POST https://api.solvecha.com/v1/recognition/geetest | Create a GeeTest slider job |
POST https://api.solvecha.com/v1/recognition/funcaptcha | Create a FunCAPTCHA tile job |
POST https://api.solvecha.com/v1/recognition/funcaptcha_match | Create a FunCAPTCHA match job |
GET https://api.solvecha.com/v1/recognition/<type>?id=<id> | Fetch the answer of a job |
GET https://api.solvecha.com/v1/status | Plan and quota of your key |
Legacy paths
These work for older NopeCHA clients. The type field is required on the root path.
| Method and path | Purpose |
|---|---|
POST https://api.solvecha.com/ | Create a job. Put type in the body. |
GET https://api.solvecha.com/?id=<id>&key=<key> | Fetch the answer of a job |
GET https://api.solvecha.com/status?key=<key> | Plan and quota of your key |
Request and response formats
Bodies are JSON or url-encoded forms. Multipart is not supported. The body limit is 4.5 MB. Images are base64 strings in the image_data array. On the v1 paths the type comes from the path, so you can omit type.
| Field | Meaning |
|---|---|
key | Your API key, if you do not send it in a header |
type | recaptcha, textcaptcha, geetest, funcaptcha or funcaptcha_match |
task | The instruction text, for example bus. Up to 2,048 characters. |
grid | reCAPTCHA only: 3x3, 4x4 or 1x1 |
image_data | Array of base64 images. For form bodies use image_data[0], image_data[1] and so on. |
reCAPTCHA (recaptcha)
For 3x3 and 4x4 send one image of the whole grid. For 1x1 send 1 to 9 tile images. The answer has one boolean per tile, left to right, top to bottom. For 1x1 it has one boolean per image.
{"task": "bus", "grid": "3x3", "image_data": ["<BASE64 GRID>"]}{"data": [false, true, false, true, true, false, false, false, false]}Text captcha (textcaptcha)
Exactly one image and no task text. The answer is an array with one case-sensitive string.
{"image_data": ["<BASE64 IMAGE>"]}{"data": ["K7m2Q"]}GeeTest slider (geetest)
Send a task and either one complete challenge screenshot, or two images: the background first, then the transparent puzzle piece. The answer is x, the offset as a percentage of the background image width.
{"task": "slide", "image_data": ["<BASE64 BACKGROUND>", "<BASE64 PIECE>"]}{"data": {"x": 42.5}}FunCAPTCHA tile (funcaptcha)
Send the task text and one image with the 3x2 grid. The answer has one boolean per tile, in row-major order.
{"task": "Pick the sheep", "image_data": ["<BASE64 GRID>"]}{"data": [false, false, false, true, false, false]}FunCAPTCHA match (funcaptcha_match)
Send the task text and one uncropped game image. The answer has one boolean per answer tile.
{"task": "Match the number of rocks with the number on the left", "image_data": ["<BASE64 IMAGE>"]}Create response
Every create request answers with a job id.
{"data": "1234567"}Examples
Each example creates a reCAPTCHA 3x3 job and polls for the answer.
curl
KEY=YOUR_API_KEY
BASE=https://api.solvecha.com
IMG=$(base64 -w0 grid.png)
ID=$(curl -s -X POST "$BASE/v1/recognition/recaptcha" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d "{\"task\":\"bus\",\"grid\":\"3x3\",\"image_data\":[\"$IMG\"]}" | jq -r .data)
sleep 1
curl -s "$BASE/v1/recognition/recaptcha?id=$ID" -H "Authorization: Bearer $KEY"Python
import base64, time, requests
BASE = "https://api.solvecha.com"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with open("grid.png", "rb") as f:
image = base64.b64encode(f.read()).decode()
job = requests.post(f"{BASE}/v1/recognition/recaptcha", headers=headers,
json={"task": "bus", "grid": "3x3", "image_data": [image]}).json()
for _ in range(30):
time.sleep(1)
r = requests.get(f"{BASE}/v1/recognition/recaptcha", headers=headers, params={"id": job["data"]})
if r.status_code == 409: # error 14: not ready yet
continue
r.raise_for_status()
print(r.json()["data"])
breakNode
import { readFile } from "node:fs/promises";
const BASE = "https://api.solvecha.com";
const headers = { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" };
const image = (await readFile("grid.png")).toString("base64");
const job = await fetch(`${BASE}/v1/recognition/recaptcha`, {
method: "POST",
headers,
body: JSON.stringify({ task: "bus", grid: "3x3", image_data: [image] }),
}).then((r) => r.json());
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 1000));
const res = await fetch(`${BASE}/v1/recognition/recaptcha?id=${job.data}`, { headers });
if (res.status === 409) continue; // error 14: not ready yet
console.log((await res.json()).data);
break;
}Polling pattern
- POST creates the job and returns
{"data": "<id>"}. - GET the same endpoint with
id. If the job is not ready, you get error 14, Incomplete job. - Poll every 1 second, for up to about 30 seconds. When the job is ready, the GET returns the answer.
Polling requests are rate limited too. Do not poll faster than once per second per job.
Error codes
| Code | Message | v1 HTTP status |
|---|---|---|
9 | Unknown error | 500 |
10 | Invalid request | 400 |
11 | Rate limit reached | 429 |
14 | Incomplete job | 409 |
15 | Invalid key | 401 |
16 | Out of credit | 403 |
The /v1/ paths use the HTTP status above and this body:
{"error": 14, "code": 14, "message": "Incomplete job", "type": null}Legacy paths answer with HTTP 200 and this body:
{"error": 14, "message": "Incomplete job"}Status endpoint
GET https://api.solvecha.com/v1/status returns the state of your key.
{
"plan": "Basic",
"status": "Active",
"credit": 19873,
"quota": 20000,
"duration": 86400,
"lastreset": 1760486400,
"ttl": 41200,
"subscribed": 0,
"current_period_start": 1760000000,
"current_period_end": 1762600000
}| Field | Meaning |
|---|---|
plan | Name of the plan |
status | Active or Expired |
credit | Successful solves left in the current daily window |
quota | Successful solves per day for the plan |
duration | Length of the quota window in seconds |
lastreset | Unix time of the last quota reset |
ttl | Seconds until the next reset |
subscribed | Kept for compatibility with NopeCHA |
current_period_start | Unix time when the plan period began |
current_period_end | Unix time when the plan period ends |
The values above are an example.
Quota and rate limits
- Only successful solutions count against the daily quota.
- The quota resets every day at 00:00 UTC.
- No plan means no solving.
- Without quota, requests fail with error 16 until the reset.
| Plan | Successful solves per day | Create requests per minute |
|---|---|---|
| Starter | 2,000 | 60 |
| Basic | 20,000 | 300 |
| Professional | 80,000 | 600 |
| Enterprise | 200,000 | 1200 |
Migrating from NopeCHA
Replace the base URL. Use your Solvecha key. Nothing else changes: paths, fields, answers and error codes follow the NopeCHA recognition API.
# before
BASE = "https://api.nopecha.com"
KEY = "YOUR_NOPECHA_KEY"
# after
BASE = "https://api.solvecha.com"
KEY = "YOUR_API_KEY"Differences to know: only JSON and url-encoded form bodies are supported, and a key needs an active plan to solve.