Cloudflare Turnstile

Solve Cloudflare Turnstile widgets and get a cf-turnstile-response token

Availability

The Turnstile task types are enabled per API key. If /createTask answers ERROR_TASK_TYPE_INACTIVE, contact us on Discord to have them granted on your key.

Overview

Cloudflare Turnstile is a CAPTCHA replacement that runs a short browser check (managed, non-interactive or invisible widget) and returns a token the site validates server-side with Cloudflare's siteverify endpoint.

Our API loads the widget's page analysis, runs Cloudflare's challenge code over a Chrome device profile and returns the token for you to submit. The widget mode is discovered from the page: there is no mode field. When a page renders several widgets, websiteKey picks the one to solve.

Supported Task Types

Task TypeProxy RequiredDescription
TurnstileTaskProxyLessNoUses our residential proxy pool
TurnstileTaskYesRuns the whole solve through your proxy

Create Task

Endpoint: POST /createTask

Request Parameters

ParameterTypeRequiredDescription
clientKeyStringYesYour API key
task.typeStringYesTask type (see table above)
task.websiteURLStringYesThe http(s) page that renders the widget. At most 2048 characters, no credentials, public host
task.websiteKeyStringYesThe widget sitekey (8 to 64 characters of A-Z a-z 0-9 _ -, usually 0x4...)
task.actionStringNoReplaces the page's data-action for this solve (1 to 32 characters of A-Z a-z 0-9 _ -). siteverify returns it
task.cDataStringNoReplaces the page's data-cdata (1 to 255 characters of the same set). siteverify returns it as cdata
task.proxyStringTurnstileTask onlyhttp(s) proxy, no socks. See Proxy Format. Ignored on TurnstileTaskProxyLess

Validation happens before anything is billed: a bad field is answered at once with ERROR_INVALID_TASK_DATA, a proxy that is not one clean token with ERROR_PROXY_FORMAT, a websiteURL or proxy host on a non-public address with ERROR_BLOCKED_TARGET.

Basic Request

{
  "clientKey": "YOUR_API_KEY",
  "task": {
    "type": "TurnstileTaskProxyLess",
    "websiteURL": "https://example.com/login",
    "websiteKey": "0x4AAAAAAAxxxxxxxxxxxxxx"
  }
}

Request with Proxy, Action and cData

{
  "clientKey": "YOUR_API_KEY",
  "task": {
    "type": "TurnstileTask",
    "websiteURL": "https://example.com/login",
    "websiteKey": "0x4AAAAAAAxxxxxxxxxxxxxx",
    "action": "login",
    "cData": "session-42",
    "proxy": "http://user:pass@host:port"
  }
}

Response

{
  "errorId": 0,
  "taskId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Get Task Result

Endpoint: POST /getTaskResult

Request

{
  "clientKey": "YOUR_API_KEY",
  "taskId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Response (Ready)

{
  "errorId": 0,
  "status": "ready",
  "solution": {
    "token": "0.xxxxxxxxxxxxxxxxxxxxxxxx...",
    "timing": { "solveMs": 6120 }
  }
}
FieldDescription
solution.tokenThe cf-turnstile-response value. Submit it as the widget response
solution.timing.solveMsTime spent solving, in milliseconds

Turnstile returns no userAgent: the token is not bound to a User-Agent, so you can submit it from your own browser or HTTP client.

Response (Processing / Failed)

{ "errorId": 0, "status": "processing" }
{
  "errorId": 1,
  "status": "failed",
  "errorCode": "ERROR_CAPTCHA_UNSOLVABLE",
  "errorDescription": "Captcha could not be solved. Please try again."
}

A failed task is refunded automatically.

Code Examples

# Create task
curl -X POST https://api.capbypass.pro/createTask \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
      "type": "TurnstileTaskProxyLess",
      "websiteURL": "https://example.com/login",
      "websiteKey": "0x4AAAAAAAxxxxxxxxxxxxxx"
    }
  }'

# Get result
curl -X POST https://api.capbypass.pro/getTaskResult \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "YOUR_API_KEY",
    "taskId": "TASK_ID"
  }'
import requests
import time

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.capbypass.pro"

def solve_turnstile(website_url, website_key, proxy=None, action=None, cdata=None):
    task = {
        "type": "TurnstileTask" if proxy else "TurnstileTaskProxyLess",
        "websiteURL": website_url,
        "websiteKey": website_key,
    }
    if proxy:
        task["proxy"] = proxy  # http(s) only, one token
    if action:
        task["action"] = action
    if cdata:
        task["cData"] = cdata

    created = requests.post(f"{BASE_URL}/createTask", json={
        "clientKey": API_KEY,
        "task": task,
    }).json()
    if created.get("errorId"):
        raise Exception(f"{created['errorCode']}: {created.get('errorDescription')}")

    deadline = time.time() + 70
    while time.time() < deadline:
        result = requests.post(f"{BASE_URL}/getTaskResult", json={
            "clientKey": API_KEY,
            "taskId": created["taskId"],
        }).json()

        if result.get("status") == "ready":
            return result["solution"]["token"]

        if result.get("errorId"):
            raise Exception(f"{result['errorCode']}: {result.get('errorDescription')}")

        time.sleep(2)

    raise TimeoutError("Turnstile task did not finish in time")

# Usage
token = solve_turnstile("https://example.com/login", "0x4AAAAAAAxxxxxxxxxxxxxx")
print(f"Token: {token[:50]}...")
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.capbypass.pro';

async function post(path, body) {
  const res = await fetch(`${BASE_URL}${path}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  return res.json();
}

async function solveTurnstile(websiteURL, websiteKey, { proxy, action, cData } = {}) {
  const task = {
    type: proxy ? 'TurnstileTask' : 'TurnstileTaskProxyLess',
    websiteURL,
    websiteKey,
  };
  if (proxy) task.proxy = proxy; // http(s) only, one token
  if (action) task.action = action;
  if (cData) task.cData = cData;

  const created = await post('/createTask', { clientKey: API_KEY, task });
  if (created.errorId) throw new Error(`${created.errorCode}: ${created.errorDescription}`);

  const deadline = Date.now() + 70_000;
  while (Date.now() < deadline) {
    const result = await post('/getTaskResult', { clientKey: API_KEY, taskId: created.taskId });
    if (result.status === 'ready') return result.solution.token;
    if (result.errorId) throw new Error(`${result.errorCode}: ${result.errorDescription}`);
    await new Promise((r) => setTimeout(r, 2000));
  }
  throw new Error('Turnstile task did not finish in time');
}

// Usage
const token = await solveTurnstile('https://example.com/login', '0x4AAAAAAAxxxxxxxxxxxxxx');

How to Find the Website Key

The sitekey is the value the page passes to the Turnstile widget.

  1. HTML attribute: look for data-sitekey on the widget element:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxxxxxxxxxxxx"></div>
  1. JavaScript render: sites that render programmatically pass it to turnstile.render():
turnstile.render('#container', { sitekey: '0x4AAAAAAAxxxxxxxxxxxxxx' });
  1. Network tab: filter for challenges.cloudflare.com. The sitekey is part of the widget frame URL.

action and cData are optional. If the page sets data-action or data-cdata, you can pass the same values so that siteverify reports them back, or override them.

Submitting the Token

Put the token in the form field the widget fills, cf-turnstile-response, then submit the form (or pass it to the site's own callback):

document.querySelector('[name="cf-turnstile-response"]').value = token;

The site then validates the token with Cloudflare. For your own tests, you can do the same call yourself:

curl https://challenges.cloudflare.com/turnstile/v0/siteverify \
  -d "secret=YOUR_SITE_SECRET" \
  -d "response=TOKEN"

The answer contains "success": true and, when you passed them, the action and cdata of the solve.

Typical Solve Time

  • Typical: 5 - 15 seconds. A busy platform can take 10 - 30 seconds.
  • Maximum timeout: about 60 seconds. A task that does not finish fails with ERROR_TIMEOUT and is refunded.

A first solve for a site is slower than the next ones: the page analysis (about 1 second) is cached per page and sitekey for 90 minutes, in Redis, so it also survives our deploys. Sites you solve regularly almost never pay it again.

Queue and Capacity

Tasks wait in a queue and are solved one at a time per worker slot; the platform runs 3 slots. There is no limit on how many tasks one key may submit at once, but everything past the slots waits.

When the estimated wait for a new task would exceed 5 minutes, the task is refused immediately with ERROR_TASK_QUEUE_FULL and refunded. That is a capacity signal, not a bug in your request: retry later, and open a ticket if you need sustained volume.

Error Codes

Error CodeDescription
ERROR_KEY_DOES_NOT_EXISTInvalid API key
ERROR_ZERO_BALANCEInsufficient balance
ERROR_CAPTCHA_UNSOLVABLEChallenge could not be solved
ERROR_TASK_NOT_FOUNDTask ID not found
ERROR_INVALID_TASK_DATAMissing or invalid parameters
ERROR_PROXY_NOT_DEFINEDProxy required for a non-ProxyLess task type — use the ProxyLess variant or supply task.proxy
ERROR_PROXY_FORMATMalformed proxy: it must be one token with no whitespace, such as host:port:user:pass or a proxy URL. See Proxy Format in the API Reference
ERROR_BLOCKED_TARGETThe target URL or your proxy host is, or resolves to, a non-public (internal or reserved) address
ERROR_PROXY_CONNECTION_FAILEDCould not connect through your proxy (refused, unreachable, or bad credentials) - check the proxy is alive and reachable
ERROR_PROXY_BANNEDThe target blocked your proxy IP (datacenter or flagged) - use a residential or mobile proxy
ERROR_INVALID_DEVELOPER_KEYThe provided developerKey is invalid or disabled
ERROR_WRONG_TASK_TYPEWrong task type for this site (e.g., standard vs enterprise)
ERROR_TIMEOUTTask exceeded timeout
ERROR_TASK_QUEUE_FULLServer is at capacity — retry in a few seconds
ERROR_TASK_TYPE_COMING_SOONTask type is not yet available
ERROR_TASK_TYPE_INACTIVETask type is currently disabled
ERROR_WORKER_CRASHEDSolver process exited mid-solve — balance refunded, safe to retry
ERROR_INTERNALInternal server error

What the codes mean for Turnstile:

Error CodeCause
ERROR_INVALID_TASK_DATAMissing or malformed field, no proxy on TurnstileTask, a socks proxy, or a sitekey Cloudflare rejects (no widget on the page, invalid or retired sitekey)
ERROR_PROXY_FORMATThe proxy is not one clean token. See Proxy Format
ERROR_BLOCKED_TARGETwebsiteURL or your proxy host is, or resolves to, a non-public address
ERROR_PROXY_CONNECTION_FAILEDTurnstileTask only: your proxy could not tunnel the request (refused, unreachable, bad credentials). Your proxy is never rotated, so the first refusal ends the task: check the line before you retry
ERROR_PROXY_BANNEDTurnstileTask only: the target answered 403 through your proxy, its IP is blocked
ERROR_TIMEOUTThe solve did not finish in time, or the task expired before a worker picked it up
ERROR_TASK_QUEUE_FULLRefused at submission: the queue would make the task wait more than 5 minutes. Nothing was solved, the reservation is refunded
ERROR_CAPTCHA_UNSOLVABLECloudflare rejected every attempt, or an interactive widget could not be completed
ERROR_TASK_TYPE_INACTIVEYour key has no beta grant for this task type

Every failed task is refunded. Retry ERROR_TIMEOUT, ERROR_TASK_QUEUE_FULL and ERROR_CAPTCHA_UNSOLVABLE later with the same task. Do not retry ERROR_PROXY_CONNECTION_FAILED or ERROR_PROXY_BANNED on the same proxy line.

Best Practices

Tokens are single-use and short-lived

A Turnstile token is valid for about 5 minutes and for one siteverify call only. Request it right before you submit the form, and never reuse it.

  1. Match the page URL - use the exact page that renders the widget. The site analysis is cached per page and sitekey.

  2. Pass the same action and cData as the page - sites that check them in siteverify reject a token with a different value.

  3. Prefer a residential proxy for TurnstileTask - a datacenter address is sent to the interactive stage, which is slower.

  4. Validate your proxy string - one token, no spaces: see Proxy Format. A whole line copied from a proxy list is refused.

Next Steps

On this page