Skip to content
ZeroCaptcha

2Captcha format

A client written for 2Captcha’s API v1 solves Cloudflare Turnstile with ZeroCaptcha after two changes: its base URL, to our API’s address, and its key, to your ZeroCaptcha key (zc_live_…). It calls in.php to create a task and res.php to read the result or your balance, as 2Captcha documents them.

A task made this way is priced, held and charged exactly as one made with REST or the createTask format: the same prices, the same checks, and nothing charged unless it is solved.

ZeroCaptcha is not affiliated with 2Captcha. Its name appears here only to say which format this is.

GET /in.php with the parameters in the query string, or POST /in.php with them as a form (application/x-www-form-urlencoded or multipart/form-data) or a JSON object. Parameters in the query string win over the same ones in the body.

Terminal window
# action and data are the widget's data-action and data-cdata (or the action and cData options of
# turnstile.render()); leave out any the widget does not set.
curl -H "Idempotency-Key: login-2026-10-01-0001" \
"$ZEROCAPTCHA_API/in.php?key=$ZEROCAPTCHA_KEY&method=turnstile&sitekey=0x4AAAAAAAB1cD2eF3gH4iJ5&pageurl=https%3A%2F%2Fshop.example.com%2Flogin&action=login&data=session-7f3a9c2e&json=1"
{ "status": 1, "request": "10000004821" }
Parameter Required What it is
key Yes Your API key, zc_live_….
method Yes turnstile. Any other method is ERROR_BAD_PARAMETERS.
sitekey Yes The widget’s site key.
pageurl Yes The full URL of the page with the widget: a public http or https page.
action When the widget sets one The widget’s action: its data-action, or action in turnstile.render.
data When the widget sets one The widget’s cData: its data-cdata, or cData in turnstile.render. Many sites check both when they verify the token; see action and cData.
pingback No A URL to call with the result when the task ends. See Polling and callbacks.
json No 1 for JSON replies; 0, the default, for plain text.
proxy No Your proxy as login:password@host:port or host:port; the task is then solved through it.
proxytype No HTTP, the default, or HTTPS. SOCKS is not supported yet (ERROR_PROXY_FORMAT).
soft_id, header_acao, pagedata, userAgent No Accepted and ignored. This format does not serve Cloudflare challenge pages; use a challenge task in REST or the createTask format.

The reply is OK|<task id> in plain text, or {"status": 1, "request": "<task id>"} with json=1. An Idempotency-Key header works as it does on POST /v1/tasks: sending the same one again returns the first task instead of making another.

GET /res.php?key=…&action=get&id=<task id>, every 2 seconds until it is ready:

Reply (plain text) Reply with json=1 Meaning
CAPCHA_NOT_READY {"status": 0, "request": "CAPCHA_NOT_READY"} Still running: ask again shortly.
OK|<token> {"status": 1, "request": "<token>"} Solved and charged. The token works once, for 300 seconds.
ERROR_CAPTCHA_UNSOLVABLE {"status": 0, "request": "ERROR_CAPTCHA_UNSOLVABLE", …} Not solved; nothing is charged.
ERROR_TOKEN_EXPIRED {"status": 0, "request": "ERROR_TOKEN_EXPIRED", …} Solved and charged, but its token has expired.

action=get2 answers OK|<token>|<price> instead, with what the task cost in US dollars, or "price" beside the token with json=1.

action=get with ids in place of id reads up to 100 tasks at once: ids=10000004821,10000004822 answers each one’s token, CAPCHA_NOT_READY or error code, in the order asked, joined by |, such as CAPCHA_NOT_READY|0.AbC…|ERROR_CAPTCHA_UNSOLVABLE, or the same text in request with json=1.

action=getbalance answers your available balance in US dollars, such as 12.3456, or {"status": 1, "request": "12.3456"} with json=1.

Once a task is solved, GET /res.php?key=…&action=reportbad&id=<task id> says the site refused its token, and action=reportgood says it took it. The reply is OK_REPORT_RECORDED, or {"status": 1, "request": "OK_REPORT_RECORDED"} with json=1.

A report is recorded against the task, where our staff read it to find sites and settings that fail. Nothing is refunded: a task is charged only when it is solved, and every charge is final. Each task takes one report: a second is ERROR_DUPLICATE_REPORT, and a report of a task that was not solved, which has no token to judge, is ERROR_REPORT_NOT_RECORDED.

With json=1, every failure carries error_text, which says what it means and what to do.

Terminal window
# Submit. action and data are the widget's data-action and data-cdata, or the action and cData
# options of turnstile.render(): leave out any the widget does not set. Add
# -d proxy=user:pass@proxy.example.net:8080 -d proxytype=HTTP to solve through your own proxy, and
# --data-urlencode pingback=https://hooks.example.com/zerocaptcha to be called when it ends.
reply=$(curl -s "$ZEROCAPTCHA_API/in.php" -H "Idempotency-Key: $(uuidgen)" \
-d key="$ZEROCAPTCHA_KEY" -d method=turnstile -d sitekey=0x4AAAAAAAB1cD2eF3gH4iJ5 \
--data-urlencode pageurl=https://shop.example.com/login -d action=login -d data=session-7f3a9c2e -d json=1)
# status 0 is a refusal: request names the code, error_text says what to do.
if [ "$(jq -r .status <<<"$reply")" != 1 ]; then echo "$reply" >&2; exit 1; fi
TASK_ID=$(jq -r .request <<<"$reply")
# Then every 2 seconds, until status is 1 (the token) or request is no longer CAPCHA_NOT_READY:
curl -s "$ZEROCAPTCHA_API/res.php?key=$ZEROCAPTCHA_KEY&action=get&id=$TASK_ID&json=1"

Every reply is HTTP 200, with the code in place of the result. Only a failure outside the format, such as a body over the size limit or the service shedding load, answers with an HTTP error and a problem document.

2Captcha format error codes
CodeWhenWhat to do
ERROR_WRONG_USER_KEYThe key is missing or is not a ZeroCaptcha key.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_KEY_DOES_NOT_EXISTThe key is unknown, revoked or expired.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_IP_NOT_ALLOWEDThe key's allowlist does not include this address.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_ACCESS_DENIEDThe key lacks the scope: tasks to create and read tasks, balance for getbalance.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_ZERO_BALANCEYour available balance does not cover the task. Add funds.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_SPEND_CAP_REACHEDThe key's daily spend cap is reached.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_PAGEURLpageurl is missing, or is not a public http or https page.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_BAD_PARAMETERSA parameter is missing or wrong: method, sitekey, pingback or action; or res.php was asked for a challenge-page task, which this format cannot carry.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_PROXY_FORMATThe proxy is not login:password@host:port or host:port, is SOCKS, or is not public.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_DOMAIN_BLOCKEDThe site is on our blocklist.Stop: do not send it again. Tell the user; a person must act (support, or not using this site).
ERROR_ACCOUNT_SUSPENDEDThe account is suspended.Stop: do not send it again. Tell the user; a person must act (support, or not using this site).
ERROR_NO_SLOT_AVAILABLEThe queue is full for a moment: send the task again shortly.Wait for Retry-After (or 2 to 5 seconds when absent), then send the same request again.
ERROR_IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used for a different task.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_IDEMPOTENCY_KEY_IN_USEThe first request with this Idempotency-Key is still being served: send it again shortly.Wait for Retry-After (or 2 to 5 seconds when absent), then send the same request again.
ERROR_SERVICE_UNAVAILABLEWe could not serve the request just now: try again shortly.Retry the same request with exponential backoff (a create with the same Idempotency-Key); honour Retry-After when present.
MAX_USER_TURNin.php is called too often: wait the seconds in Retry-After.Wait for Retry-After (or 2 to 5 seconds when absent), then send the same request again.
ERROR_EMPTY_ACTIONres.php was called without action.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_WRONG_ID_FORMATid is not a task ID.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_WRONG_CAPTCHA_IDNo task of this account has that ID.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_REPORT_NOT_RECORDEDreportbad or reportgood named a task that did not succeed.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_DUPLICATE_REPORTreportbad or reportgood named a task reported already: one report per task.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_CAPTCHA_UNSOLVABLEThe task was not solved, or not before its deadline; nothing is charged.The task is over and nothing more will come of it: create a new task if you still need a token.
ERROR_BAD_PROXYYour proxy pointed at an address tasks cannot use; nothing is charged.Do not retry as is: fix the request, key, balance or setting the message names, then try again.
ERROR_TOKEN_EXPIREDThe task was solved and charged, but its token has expired.The task is over and nothing more will come of it: create a new task if you still need a token.
ERROR: 1005res.php is called too often: wait the seconds in Retry-After.Wait for Retry-After (or 2 to 5 seconds when absent), then send the same request again.

in.php has no rate budget by default, as task creation has none; res.php reads share the read budgets with the other formats. Over one, the reply is MAX_USER_TURN or ERROR: 1005, with Retry-After.

  • Only method=turnstile. Other CAPTCHA types are not offered, and Cloudflare challenge pages need the createTask format or REST, whose replies carry the user agent a clearance needs.
  • pingback needs no registration: any public URL works, and each call is signed so you can check it came from us.
  • reportbad never refunds: it is recorded for our staff, and every charge is final.
  • There is no free trial or test key: every task is real, and charged only when it is solved.

Moving an existing client over? See Migrate a 2Captcha client.