# 2Captcha format API

> 2Captcha's in.php and res.php for Turnstile, so a 2Captcha client works after changing only its base URL and key. Priced and charged as the other dialects are. Every reply is HTTP 200, as plain text or, with `json=1`, JSON; only a failure outside the endpoint is a problem document.

Source: https://zerocaptcha.io/docs/reference/api/2captcha

## Submit a task (2Captcha)

`GET /in.php` (`twoCaptchaSubmit`)

2Captcha's `in.php` for Cloudflare Turnstile: `method=turnstile` with
`sitekey` and `pageurl`, and optionally `action`, `data`, `proxy` and
`pingback`. The reply is `OK|<task id>`, or `{"status": 1, "request":
"<task id>"}` with `json=1`; poll `res.php` with the ID. Priced, held and
charged as `POST /v1/tasks` is; an `Idempotency-Key` header works as it
does there.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `key` | query | yes | Your API key, `zc_live_…`. A missing or malformed one is `ERROR_WRONG_USER_KEY`, an unknown or revoked one `ERROR_KEY_DOES_NOT_EXIST`. |
| `method` | query | yes | `turnstile`; any other method is `ERROR_BAD_PARAMETERS`. |
| `sitekey` | query | yes | The widget's site key. |
| `pageurl` | query | yes | The page the widget is on: a public http or https page, as `websiteURL` is on REST. Missing or invalid, `ERROR_PAGEURL`. |
| `action` | query | no | The widget's action, if it sets one. |
| `data` | query | no | The widget's cData, if it sets one. |
| `pingback` | query | no | Where to POST the result once the task ends: 2Captcha's pingback form, `id` and `code`, signed with your callback secret. Any public URL, with no registration. |
| `json` | query | no | `1` for JSON replies; `0`, the default, for plain text. |
| `proxy` | query | no | Your proxy as `login:password@host:port` or `host:port`; the task then runs through it. |
| `proxytype` | query | no | `HTTP`, the default, or `HTTPS`. SOCKS is not supported yet (`ERROR_PROXY_FORMAT`). |
| `soft_id` | query | no | Accepted and ignored. |
| `header_acao` | query | no | Accepted and ignored: this service sends no CORS headers. |
| `pagedata` | query | no | Accepted and ignored: Cloudflare challenge pages are not supported. |
| `userAgent` | query | no | Accepted and ignored. |
| `Idempotency-Key` | header | no | Your ID for this task, 1 to 255 visible ASCII characters, as on `POST /v1/tasks`. |

Responses:

- 200 OK: `OK|<task id>`, or an error code such as `ERROR_ZERO_BALANCE`; with `json=1`, the same as 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 `Retry-After`) or a fault in the server (500). Every other failure is HTTP 200 with an error code.

```bash
curl https://api.zerocaptcha.io/in.php?key=key&method=method&sitekey=sitekey&pageurl=pageurl \
  -H "Idempotency-Key: order-4521-attempt-1"
```

```js
const response = await fetch("https://api.zerocaptcha.io/in.php?key=key&method=method&sitekey=sitekey&pageurl=pageurl", {
  headers: {
    "Idempotency-Key": "order-4521-attempt-1",
  },
});
console.log(response.status, await response.text());
```

```python
import requests

response = requests.get(
    "https://api.zerocaptcha.io/in.php?key=key&method=method&sitekey=sitekey&pageurl=pageurl",
    headers={"Idempotency-Key": "order-4521-attempt-1"},
    timeout=30,
)
print(response.status_code, response.text)
```

## Submit a task by POST (2Captcha)

`POST /in.php` (`twoCaptchaSubmitForm`)

`in.php` as a POST, the way 2Captcha's own clients send it: the
parameters as a form (`application/x-www-form-urlencoded` or
`multipart/form-data`) or a JSON object, and any in the query string too,
which win. The replies are those of `GET /in.php`.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `Idempotency-Key` | header | no | Your ID for this task, 1 to 255 visible ASCII characters, as on `POST /v1/tasks`. |

Responses:

- 200 OK: `OK|<task id>`, or an error code such as `ERROR_ZERO_BALANCE`; with `json=1`, the same as 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 `Retry-After`) or a fault in the server (500). Every other failure is HTTP 200 with an error code.

```bash
curl -X POST https://api.zerocaptcha.io/in.php \
  -H "Idempotency-Key: order-4521-attempt-1"
```

```js
const response = await fetch("https://api.zerocaptcha.io/in.php", {
  method: "POST",
  headers: {
    "Idempotency-Key": "order-4521-attempt-1",
  },
});
console.log(response.status, await response.text());
```

```python
import requests

response = requests.post(
    "https://api.zerocaptcha.io/in.php",
    headers={"Idempotency-Key": "order-4521-attempt-1"},
    timeout=30,
)
print(response.status_code, response.text)
```

## Get a result or the balance (2Captcha)

`GET /res.php` (`twoCaptchaResult`)

2Captcha's `res.php`. `action=get&id=<task id>` answers
`CAPCHA_NOT_READY` while the task runs, then `OK|<token>`, or an error
code such as `ERROR_CAPTCHA_UNSOLVABLE` (nothing charged) or
`ERROR_TOKEN_EXPIRED`; `action=get2` adds the price, `OK|<token>|<price>`.
`action=get&ids=<id>,<id>,…` reads up to 100 tasks at once: each one's
token, `CAPCHA_NOT_READY` or error code, in order, joined by `|`.
`action=reportbad` or `reportgood` with `id` records whether the site
took the token (`OK_REPORT_RECORDED`): recorded for our staff, never
refunded, as tasks are final. `action=getbalance` answers the available
balance in US dollars, such as `12.3456`. With `json=1`, each as
`{"status": …, "request": …}`. Reads share their budgets with REST; over
one, the reply is `ERROR: 1005` with `Retry-After`.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `key` | query | yes | Your API key, as for in.php. |
| `action` | query | yes | `get` for a task's token (or several tasks' with `ids`), `get2` for its token and price, `reportbad` or `reportgood` to say whether the site took its token, or `getbalance` for the available balance. Missing, `ERROR_EMPTY_ACTION`. |
| `id` | query | no | The task's ID, as in.php gave it, for `get`, `get2`, `reportbad` and `reportgood`; a task's UUID from another format works too. Not a task ID, `ERROR_WRONG_ID_FORMAT`; no task of this key's account, `ERROR_WRONG_CAPTCHA_ID`. |
| `ids` | query | no | With `action=get`, in place of `id`: up to 100 task IDs, comma-separated. The reply is each one's answer in order, joined by `\|`, such as `CAPCHA_NOT_READY\|0.AbC…\|ERROR_CAPTCHA_UNSOLVABLE`. |
| `json` | query | no | `1` for JSON replies; `0`, the default, for plain text. |

Responses:

- 200 OK: The token, `CAPCHA_NOT_READY`, several tasks' answers, `OK_REPORT_RECORDED`, the balance, or an error code; with `json=1`, the same as 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 `Retry-After`) or a fault in the server (500). Every other failure is HTTP 200 with an error code.

```bash
curl https://api.zerocaptcha.io/res.php?key=key&action=action
```

```js
const response = await fetch("https://api.zerocaptcha.io/res.php?key=key&action=action");
console.log(response.status, await response.text());
```

```python
import requests

response = requests.get(
    "https://api.zerocaptcha.io/res.php?key=key&action=action",
    timeout=30,
)
print(response.status_code, response.text)
```
