Compatible format API
More
The createTask format other providers use, so existing clients work unchanged. Its errors come in its errorId shape, with HTTP 200; only a failure outside the endpoint, such as a body over the size limit, is a problem document.
Every operation of the API reference is generated from the contract the API serves, version 0.1.0. Replace YOUR_API_KEY in the samples with your key.
Create a task (compatible)
POST/createTask
The createTask call other providers use. The body is {"clientKey": "…", "task": {"type": "TurnstileTaskProxyless", "websiteURL": "…", "websiteKey": "…"}}; the reply is {"errorId": 0, "taskId": "…"}. An Idempotency-Key header works as it does on REST. Creations share the key's and the account's budgets, if the service sets any, with POST /v1/tasks. Over one, the reply is ERROR_RATE_LIMIT with Retry-After, and nothing is created or charged.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Key | header | string | Your ID for this task, 1 to 255 visible ASCII characters, as on |
Request body
JSON: CompatCreateRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
task required | CompatAnyTask | A Turnstile task, or a Cloudflare challenge page's, told apart by |
callbackUrl | string (uri) or null | Where to POST the result once the task ends, signed with your callback secret ( |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatCreateReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_RATE_LIMIT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/createTask \ -H "Idempotency-Key: order-4521-attempt-1" \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "TurnstileTaskProxyless", "websiteKey": "0x4AAAAAAAB1cD2eF3gH4iJ5", "websiteURL": "https://example.com/login" }}'const response = await fetch("https://api.zerocaptcha.io/createTask", { method: "POST", headers: { "Idempotency-Key": "order-4521-attempt-1", "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "task": { "type": "TurnstileTaskProxyless", "websiteKey": "0x4AAAAAAAB1cD2eF3gH4iJ5", "websiteURL": "https://example.com/login" } }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/createTask", headers={"Idempotency-Key": "order-4521-attempt-1"}, json={ "clientKey": "YOUR_API_KEY", "task": { "type": "TurnstileTaskProxyless", "websiteKey": "0x4AAAAAAAB1cD2eF3gH4iJ5", "websiteURL": "https://example.com/login", }, }, timeout=30,)print(response.status_code, response.text)Report how a token did (CapSolver)
POST/feedbackTask
CapSolver's feedbackTask: {"clientKey": "…", "taskId": "…", "result": {"invalid": true}} says the site refused the token, false that it took it; invalid may also come beside result. Recorded for our staff, never refunded. Without invalid, ERROR_INVALID_REQUEST.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatFeedbackRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
result required | CompatFeedbackResult | What the site made of the token. |
taskId required | string (uuid) | The task's ID, as createTask gave it. |
Fields of result (CompatFeedbackResult)
| Field | Type | Description |
|---|---|---|
invalid required | boolean |
|
code | integer or null | Accepted and ignored. |
message | string or null | Accepted and ignored. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatReportReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_INVALID_REQUEST, ERROR_NO_SUCH_CAPCHA_ID, ERROR_REPORT_NOT_RECORDED, ERROR_DUPLICATE_REPORT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/feedbackTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "result": { "invalid": true }, "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/feedbackTask", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "result": { "invalid": true }, "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/feedbackTask", json={ "clientKey": "YOUR_API_KEY", "result": { "invalid": True, }, "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)Get the balance (compatible)
POST/getBalance
{"clientKey": "…"}; the reply is {"errorId": 0, "balance": 12.3456}, the available balance in US dollars, printed exactly. Reads share their budgets with REST; over one, the reply is ERROR_RATE_LIMIT with Retry-After.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatBalanceRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The balance, or an error in the dialect's shape. | CompatBalanceReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_RATE_LIMIT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/getBalance \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY"}'const response = await fetch("https://api.zerocaptcha.io/getBalance", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/getBalance", json={ "clientKey": "YOUR_API_KEY", }, timeout=30,)print(response.status_code, response.text)Get a task's result (compatible)
POST/getTaskResult
{"clientKey": "…", "taskId": "…"}. While the task runs the reply is {"errorId": 0, "status": "processing"}; once solved it is status: "ready" with solution.token and cost, and for a challenge page solution.userAgent and solution.cookies too. A failed task, or one whose token expired, replies with errorId: 1 and its errorCode. Polls share the budgets for reads with REST; over one, the reply is ERROR_RATE_LIMIT with Retry-After.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatResultRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
taskId required | string (uuid) | The task's ID, as createTask gave it. One that names no task of this key's account is |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The task's state, or an error in the dialect's shape. | CompatResultReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_RATE_LIMIT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/getTaskResult \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/getTaskResult", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/getTaskResult", json={ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)Report a token that worked (2Captcha)
POST/reportCorrect
2Captcha's reportCorrect: {"clientKey": "…", "taskId": "…"} says the site took the solved task's token. Recorded for our staff; one report per task, on a task that succeeded.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatReportRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
taskId required | string (uuid) | The task's ID, as createTask gave it. One that names no task of this key's account is |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatReportReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_NO_SUCH_CAPCHA_ID, ERROR_REPORT_NOT_RECORDED, ERROR_DUPLICATE_REPORT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/reportCorrect \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/reportCorrect", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/reportCorrect", json={ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)Report a token that worked (Anti-Captcha)
POST/reportCorrectRecaptcha
Anti-Captcha's reportCorrectRecaptcha: the same as /reportCorrect.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatReportRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
taskId required | string (uuid) | The task's ID, as createTask gave it. One that names no task of this key's account is |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatReportReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_NO_SUCH_CAPCHA_ID, ERROR_REPORT_NOT_RECORDED, ERROR_DUPLICATE_REPORT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/reportCorrectRecaptcha \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/reportCorrectRecaptcha", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/reportCorrectRecaptcha", json={ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)Report a refused token (2Captcha)
POST/reportIncorrect
2Captcha's reportIncorrect: {"clientKey": "…", "taskId": "…"} says the site refused the solved task's token. It is recorded for our staff, who watch the solvers' quality with it; tasks are final, so it refunds nothing. One report per task, on a task that succeeded.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatReportRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
taskId required | string (uuid) | The task's ID, as createTask gave it. One that names no task of this key's account is |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatReportReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_NO_SUCH_CAPCHA_ID, ERROR_REPORT_NOT_RECORDED, ERROR_DUPLICATE_REPORT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/reportIncorrect \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/reportIncorrect", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/reportIncorrect", json={ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)Report a refused token (Anti-Captcha)
POST/reportIncorrectRecaptcha
Anti-Captcha's reportIncorrectRecaptcha, which its clients send for a token task: the same as /reportIncorrect. Recorded, never refunded.
Authentication: Your API key, as clientKey in the JSON body, as other providers' clients send it.
Request body
JSON: CompatReportRequest.
The server reads the body more leniently than its schema, and decides: it takes JSON whatever the Content-Type, as some clients send text/plain, a number wherever it expects text, and null for an absent field, and it ignores fields it does not use.
| Field | Type | Description |
|---|---|---|
clientKey required | string | Your API key, |
taskId required | string (uuid) | The task's ID, as createTask gave it. One that names no task of this key's account is |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK |
| CompatReportReply (application/json) |
| Any other status | Only a failure outside the dialect, as RFC 9457 problem details: a body over the size limit (413), a request that ran out of time (504), this instance shedding load (503, with | Problem (application/problem+json) |
Error codes
ERROR_NO_SUCH_CAPCHA_ID, ERROR_REPORT_NOT_RECORDED, ERROR_DUPLICATE_REPORT. The errors reference says what each means, whether a retry helps and what it costs.
Sample
curl -X POST https://api.zerocaptcha.io/reportIncorrectRecaptcha \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'const response = await fetch("https://api.zerocaptcha.io/reportIncorrectRecaptcha", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }),});console.log(response.status, await response.text());import requests
response = requests.post( "https://api.zerocaptcha.io/reportIncorrectRecaptcha", json={ "clientKey": "YOUR_API_KEY", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", }, timeout=30,)print(response.status_code, response.text)