# createTask and getTaskResult: The Captcha API Format Explained

> How the createTask, getTaskResult and getBalance format works: request bodies, replies, errorId, polling, and ZeroCaptcha's Cloudflare Turnstile tasks.

- Source: https://zerocaptcha.io/guides/createtask-gettaskresult-explained
- Published: 2026-09-30
- Updated: 2026-10-01
- Author: ZeroCaptcha Engineering

`createTask` and `getTaskResult` are the most widely used request format among CAPTCHA-solving
services. You create a task with one JSON call, get a task ID back at once, then ask for the
result until it is ready. ZeroCaptcha implements this format for Cloudflare Turnstile and
Cloudflare challenge pages, so clients
written for it work after changing the host and the key. This guide explains each call, the
replies, and the conventions that trip people up.

## The three calls

All three are `POST` requests with a JSON body to the API host, and all three carry your key in
the body as `clientKey`:

| Call | Body | Reply |
| --- | --- | --- |
| `/createTask` | `clientKey`, `task` | `errorId`, `taskId` |
| `/getTaskResult` | `clientKey`, `taskId` | `errorId`, `status`, and `solution` once ready |
| `/getBalance` | `clientKey` | `errorId`, `balance` |

## Creating a task

```sh
# metadata holds 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. The Idempotency-Key makes a retried
# create return the same task.
curl -s "$ZEROCAPTCHA_API/createTask" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "clientKey": "'"$ZEROCAPTCHA_KEY"'",
    "task": {
      "type": "TurnstileTaskProxyless",
      "websiteURL": "https://shop.example.com/login",
      "websiteKey": "0x4AAAAAAAB1cD2eF3gH4iJ5",
      "metadata": {"action": "login", "cdata": "session-7f3a9c2e"}
    }
  }'
```

```json
{ "errorId": 0, "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b" }
```

The `task` object describes the challenge. For Turnstile:

- `type`: `TurnstileTaskProxyless`, or `TurnstileTask` to solve through your own proxy. The
  aliases `AntiTurnstileTaskProxyLess` and `AntiTurnstileTask` work too.
- `websiteURL` (or `websiteUrl`) and `websiteKey`: the page and the widget's sitekey.
- `action` and `cdata`: when the widget sets them. See
  [Cloudflare Turnstile action and cData](https://zerocaptcha.io/guides/cloudflare-turnstile-action-and-cdata).
- Proxy fields, for `TurnstileTask`: see [Solve Cloudflare Turnstile with a proxy](https://zerocaptcha.io/guides/solve-cloudflare-turnstile-with-a-proxy).

Fields the task does not use, such as `userAgent`, are accepted and ignored, so a client that
sends extras still works. The task's price is held on your balance when it is created.

## Polling for the result

```sh
curl -s "$ZEROCAPTCHA_API/getTaskResult" \
  -H "Content-Type: application/json" \
  -d '{"clientKey": "'"$ZEROCAPTCHA_KEY"'", "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b"}'
```

While the task runs, the reply is:

```json
{ "errorId": 0, "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b", "status": "processing" }
```

When it is solved:

```json
{
  "errorId": 0,
  "taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b",
  "status": "ready",
  "solution": { "token": "0.xT4…", "type": "turnstile" },
  "cost": "0.000800",
  "createTime": 1790762400,
  "endTime": 1790762404,
  "solveCount": 1,
  "expiresAt": "2026-09-30T10:05:04Z"
}
```

`cost` is what the task cost in US dollars, as a string with six decimals. `expiresAt` says when the token stops working: Turnstile tokens are valid for 300
seconds and work once. If the task failed, the reply has `errorId` 1 and an `errorCode` such as
`ERROR_CAPTCHA_UNSOLVABLE`, and nothing is charged.

## The conventions

- **Every reply is HTTP 200.** Success or failure is in `errorId`: 0 for success, 1 for an error
  with `errorCode` and `errorDescription`. Only failures outside the format, such as a body over
  the size limit, answer with an HTTP error.
- **Task IDs are strings.** ZeroCaptcha's task IDs are UUIDs. A client that keeps the ID as text
  passes it back unchanged; one that parses it as a number needs that changed.
- **The body is JSON whatever the Content-Type.** Some clients send JSON as `text/plain`; that
  works.
- **Poll every one or two seconds.** `getTaskResult` and `getBalance` share a budget; calling
  them far faster is answered with `ERROR_RATE_LIMIT` and a `Retry-After` header.

## Retrying createTask safely

A lost reply to `createTask` leaves you unsure whether the task exists. Send an `Idempotency-Key`
header with every `createTask`: the same key and body within 24 hours returns the first task
instead of making a second. [Idempotency keys for captcha tasks](https://zerocaptcha.io/guides/idempotency-keys-for-captcha-tasks)
shows the pattern.

## Callbacks instead of polling

Add `callbackUrl` beside `task`, and ZeroCaptcha posts the `getTaskResult` reply to that URL when
the task ends, signed so you can check it. Polling still works alongside it. See
[Captcha solver callbacks](https://zerocaptcha.io/guides/captcha-solver-callbacks).

## The other two formats

The same tasks, prices and charges are available two other ways:

- **2Captcha's `in.php` and `res.php`**, for clients written for that API: see
  [the 2Captcha format](https://zerocaptcha.io/docs/2captcha).
- **REST v1**, with `POST /v1/tasks`, a bearer key, an `Idempotency-Key` header and RFC 9457
  problem details: see the [Tasks API reference](https://zerocaptcha.io/docs/reference/api/tasks).

The [compatible format reference](https://zerocaptcha.io/docs/reference/api/compatible) documents every field, and the
[Cloudflare Turnstile solver](https://zerocaptcha.io/cloudflare-turnstile-solver) page shows a complete program in each supported language.

## Questions

### Why does getTaskResult answer HTTP 200 even for errors?

That is the format's convention: the HTTP status says the call was received, and errorId says whether it worked. Check both, as the quickstart samples do.

### How often should I call getTaskResult?

Every one or two seconds is enough. Polling faster only spends your read budget, and a callback removes polling altogether.

### Is the createTask format the only way to use ZeroCaptcha?

No. The same tasks are available through 2Captcha's in.php and res.php, and through a REST API with bearer keys and problem details.
