2captcha legacy API
For software written for the 2captcha in.php / res.php protocol, Noptcha accepts the same requests on
https://api.noptcha.com/in.php and https://api.noptcha.com/res.php. Use a Noptcha key as key. For new
integrations prefer the native API or the compatibility API.
Tasks created here are ordinary tasks: same prices, limits and sandbox, and the task ID also works with
/getTaskResult and GET /v1/tasks/{id}.
Requests
Section titled “Requests”GETorPOST. Parameters may be in the query string, a form body (application/x-www-form-urlencodedormultipart/form-datawith the image infile), or a JSON body. A body parameter overrides the same one in the query.- Add
json=1for JSON responses:{"status": 1, "request": "…"}on success,{"status": 0, "request": "ERROR_…", "error_text": "…"}on errors. Without it, responses are plain text:OK|<value>,CAPCHA_NOT_READYor an error code. header_acao=1addsAccess-Control-Allow-Origin: *;OPTIONSrequests to both endpoints return 204 (CORS preflight).- Responses are HTTP 200, including errors and rate limits; read the body. One exception: when the service is
overloaded, a
res.phprequest that does not haveactionin the query string (for example a POST withactionin the form body) gets HTTP 503 withRetry-After: 1andERROR_NO_SLOT_AVAILABLE. Wait and retry.
Create a task: in.php
Section titled “Create a task: in.php”export NOPTCHA_KEY=nop_test_... # your test or live keycurl -s "https://api.noptcha.com/in.php" \ -d "key=$NOPTCHA_KEY" \ -d 'method=userrecaptcha' \ -d 'googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-' \ -d 'pageurl=https://www.google.com/recaptcha/api2/demo' \ -d 'json=1'import os
import requests
API = "https://api.noptcha.com"KEY = os.environ["NOPTCHA_KEY"]
# a form body keeps the key out of the URLr = requests.post(f"{API}/in.php", data={ "key": KEY, "method": "userrecaptcha", "googlekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", "pageurl": "https://www.google.com/recaptcha/api2/demo", "json": "1",}, timeout=30)print(r.json())const API = 'https://api.noptcha.com';const KEY = process.env.NOPTCHA_KEY;
// a form body keeps the key out of the URLconst res = await fetch(`${API}/in.php`, { method: 'POST', body: new URLSearchParams({ key: KEY, method: "userrecaptcha", googlekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", pageurl: "https://www.google.com/recaptcha/api2/demo", json: "1", }),});console.log(await res.json());The response is the task ID: OK|7259140378796032, or {"status": 1, "request": "7259140378796032"} with json=1.
Only errors that can be detected before the task is created (key, balance, rate limits, parameter format) are
returned here; the outcome of the task, including failures, is always reported by res.php.
method |
Task type | Parameters |
|---|---|---|
userrecaptcha |
reCAPTCHA v2; enterprise=1: v2 Enterprise; version=v3: v3 (with enterprise=1: v3 Enterprise) |
googlekey, pageurl, invisible=1, action, min_score (v3), data-s (Enterprise), domain (recaptcha.net or google.com), cookies (K1:V1;K2:V2), userAgent |
hcaptcha |
hCaptcha | sitekey, pageurl, invisible, data (rqdata), userAgent |
turnstile |
Cloudflare Turnstile | sitekey, pageurl, action, data (cData), pagedata, userAgent |
funcaptcha |
FunCaptcha | publickey, pageurl, surl, data[key]=value, userAgent |
base64 (body) or post (file) |
Image to text | phrase, regsense, numeric (1 or 2), calc, min_len, max_len |
same, with recaptcha=1 |
reCAPTCHA image grid | textinstructions (required), recaptcharows = recaptchacols (1, 3 or 4) |
same, with recaptcha=1&img_type=funcaptcha |
FunCaptcha image | textinstructions |
Proxies: proxy=login:password@host:port (or host:port) and proxytype=HTTP|HTTPS|SOCKS4|SOCKS5. Types that do not
use a proxy (reCAPTCHA v3, images) ignore it.
Other methods (for example geetest, amazon_waf, keycaptcha, capy, canvas, coordinatescaptcha,
textcaptcha, img_type=hcaptcha) and pingback are answered with ERROR_BAD_PARAMETERS. Parameters such as
language, lang and imginstructions are ignored.
export NOPTCHA_KEY=nop_test_... # your test or live keycurl -s "https://api.noptcha.com/in.php" \ -d "key=$NOPTCHA_KEY" \ -d 'method=turnstile' \ -d 'sitekey=1x00000000000000000000AA' \ -d 'pageurl=https://example.com/signup' \ -d 'json=1'import os
import requests
API = "https://api.noptcha.com"KEY = os.environ["NOPTCHA_KEY"]
# a form body keeps the key out of the URLr = requests.post(f"{API}/in.php", data={ "key": KEY, "method": "turnstile", "sitekey": "1x00000000000000000000AA", "pageurl": "https://example.com/signup", "json": "1",}, timeout=30)print(r.json())const API = 'https://api.noptcha.com';const KEY = process.env.NOPTCHA_KEY;
// a form body keeps the key out of the URLconst res = await fetch(`${API}/in.php`, { method: 'POST', body: new URLSearchParams({ key: KEY, method: "turnstile", sitekey: "1x00000000000000000000AA", pageurl: "https://example.com/signup", json: "1", }),});console.log(await res.json());Get the result: res.php?action=get
Section titled “Get the result: res.php?action=get”export NOPTCHA_KEY=nop_test_... # your test or live keyTASK_ID=7259140378796032 # the ID you got from in.phpcurl -s "https://api.noptcha.com/res.php" \ -d "key=$NOPTCHA_KEY" \ -d 'action=get' \ -d "id=$TASK_ID" \ -d 'json=1'import os
import requests
API = "https://api.noptcha.com"KEY = os.environ["NOPTCHA_KEY"]task_id = 7259140378796032 # the task ID you got when creating the task
# a form body keeps the key out of the URLr = requests.post(f"{API}/res.php", data={ "key": KEY, "action": "get", "id": str(task_id), "json": "1",}, timeout=30)print(r.json())const API = 'https://api.noptcha.com';const KEY = process.env.NOPTCHA_KEY;const taskId = 7259140378796032; // the task ID you got when creating the task
// a form body keeps the key out of the URLconst res = await fetch(`${API}/res.php`, { method: 'POST', body: new URLSearchParams({ key: KEY, action: "get", id: String(taskId), json: "1", }),});console.log(await res.json());| Response | Meaning |
|---|---|
CAPCHA_NOT_READY |
Not finished yet. Poll again in a few seconds. |
OK|<answer> |
Solved. Tokens are returned as is (with json=1 also useragent, and respKey for hCaptcha); image text as text; image grids as click:3/8/9 (counted from 1, or No_matching_images); other answers as JSON. |
ERROR_CAPTCHA_UNSOLVABLE, ERROR_PROXY_CONNECTION_FAILED, ERROR_TOKEN_EXPIRED |
The task failed (error_text says why). Not charged. |
action=get2adds the price:OK|<answer>|<price>(withjson=1, apricefield), 6 decimal places in US dollars.action=get&ids=1,2,3reads up to 20 tasks at once; answers are joined with|in the same order, withCAPCHA_NOT_READYor an error code in the place of tasks that are not solved.
Other actions
Section titled “Other actions”action |
Response |
|---|---|
getbalance |
Available balance in US dollars, for example 12.345678 |
reportbad, reportgood (id) |
OK_REPORT_RECORDED. Reports are recorded as a quality signal; they do not refund. |
| anything else | ERROR_EMPTY_ACTION |
export NOPTCHA_KEY=nop_test_... # your test or live keycurl -s "https://api.noptcha.com/res.php" \ -d "key=$NOPTCHA_KEY" \ -d 'action=getbalance' \ -d 'json=1'import os
import requests
API = "https://api.noptcha.com"KEY = os.environ["NOPTCHA_KEY"]
# a form body keeps the key out of the URLr = requests.post(f"{API}/res.php", data={ "key": KEY, "action": "getbalance", "json": "1",}, timeout=30)print(r.json())const API = 'https://api.noptcha.com';const KEY = process.env.NOPTCHA_KEY;
// a form body keeps the key out of the URLconst res = await fetch(`${API}/res.php`, { method: 'POST', body: new URLSearchParams({ key: KEY, action: "getbalance", json: "1", }),});console.log(await res.json());Errors
Section titled “Errors”| Code | When |
|---|---|
ERROR_WRONG_USER_KEY |
the key is not in Noptcha’s format |
ERROR_KEY_DOES_NOT_EXIST |
the key is unknown, revoked or expired, lacks the scope, or the organization is suspended (error_text says which) |
ERROR_ZERO_BALANCE |
balance too low, or the key’s budget is used up |
ERROR_IP_NOT_ALLOWED |
the client IP is not in the key’s allowed ranges |
IP_BANNED |
too many invalid keys from this IP (details) |
ERROR_NO_SLOT_AVAILABLE |
creating tasks too fast, concurrency limit, or no capacity; wait a few seconds and resend |
ERROR_BAD_PARAMETERS, ERROR_WRONG_GOOGLEKEY, ERROR_GOOGLEKEY, ERROR_PAGEURL, ERROR_PROXY_FORMAT |
missing or invalid parameters, unsupported method, type not allowed for the key, pingback |
ERROR_ZERO_CAPTCHA_FILESIZE, ERROR_TOO_BIG_CAPTCHA_FILESIZE |
the image is empty, or too large (bytes, or over the pixel limit) |
ERROR_UPLOAD |
the image is not valid base64 (accepted forms) |
ERROR_IMAGE_TYPE_NOT_SUPPORTED |
the image is not a PNG, JPEG, GIF, WebP or BMP file, or is malformed or truncated |
ERROR_WRONG_ID_FORMAT, ERROR_WRONG_CAPTCHA_ID |
the id is not a number, or the task is unknown, expired or not yours |
ERROR_REPORT_NOT_RECORDED |
report for a task that is not solved |
Polling one task too often, or reading a batch beyond the rate limit, is answered with CAPCHA_NOT_READY rather than
an error, so clients keep polling.
Differences from 2captcha
Section titled “Differences from 2captcha”- Keys are Noptcha keys (
nop_live_…,nop_test_…), and only HTTPS is accepted. - No
ERROR: 1001–1005account bans; rate limits apply instead, with the codes above. - A second report for the same task also returns
OK_REPORT_RECORDED; reports never refund. - Prices (
get2) and the balance have 6 decimal places. - Not available:
pingback(usecallbackUrlorcallback_urlwith the other APIs),debug_dump, developer parameters other thansoft_id,language,lang,imginstructions. idsbatches are limited to 20 tasks.OPTIONSrequests get a CORS preflight response.- Test keys work as in the sandbox: the magic values go in
googlekey,sitekey,publickeyortextinstructions, and outcomes are reported byres.phpas for live keys.