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 Type | Proxy Required | Description |
|---|---|---|
TurnstileTaskProxyLess | No | Uses our residential proxy pool |
TurnstileTask | Yes | Runs the whole solve through your proxy |
Create Task
Endpoint: POST /createTask
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
clientKey | String | Yes | Your API key |
task.type | String | Yes | Task type (see table above) |
task.websiteURL | String | Yes | The http(s) page that renders the widget. At most 2048 characters, no credentials, public host |
task.websiteKey | String | Yes | The widget sitekey (8 to 64 characters of A-Z a-z 0-9 _ -, usually 0x4...) |
task.action | String | No | Replaces the page's data-action for this solve (1 to 32 characters of A-Z a-z 0-9 _ -). siteverify returns it |
task.cData | String | No | Replaces the page's data-cdata (1 to 255 characters of the same set). siteverify returns it as cdata |
task.proxy | String | TurnstileTask only | http(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 }
}
}| Field | Description |
|---|---|
solution.token | The cf-turnstile-response value. Submit it as the widget response |
solution.timing.solveMs | Time 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.
- HTML attribute: look for
data-sitekeyon the widget element:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxxxxxxxxxxxx"></div>- JavaScript render: sites that render programmatically pass it to
turnstile.render():
turnstile.render('#container', { sitekey: '0x4AAAAAAAxxxxxxxxxxxxxx' });- 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_TIMEOUTand 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 Code | Description |
|---|---|
ERROR_KEY_DOES_NOT_EXIST | Invalid API key |
ERROR_ZERO_BALANCE | Insufficient balance |
ERROR_CAPTCHA_UNSOLVABLE | Challenge could not be solved |
ERROR_TASK_NOT_FOUND | Task ID not found |
ERROR_INVALID_TASK_DATA | Missing or invalid parameters |
ERROR_PROXY_NOT_DEFINED | Proxy required for a non-ProxyLess task type — use the ProxyLess variant or supply task.proxy |
ERROR_PROXY_FORMAT | Malformed 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_TARGET | The target URL or your proxy host is, or resolves to, a non-public (internal or reserved) address |
ERROR_PROXY_CONNECTION_FAILED | Could not connect through your proxy (refused, unreachable, or bad credentials) - check the proxy is alive and reachable |
ERROR_PROXY_BANNED | The target blocked your proxy IP (datacenter or flagged) - use a residential or mobile proxy |
ERROR_INVALID_DEVELOPER_KEY | The provided developerKey is invalid or disabled |
ERROR_WRONG_TASK_TYPE | Wrong task type for this site (e.g., standard vs enterprise) |
ERROR_TIMEOUT | Task exceeded timeout |
ERROR_TASK_QUEUE_FULL | Server is at capacity — retry in a few seconds |
ERROR_TASK_TYPE_COMING_SOON | Task type is not yet available |
ERROR_TASK_TYPE_INACTIVE | Task type is currently disabled |
ERROR_WORKER_CRASHED | Solver process exited mid-solve — balance refunded, safe to retry |
ERROR_INTERNAL | Internal server error |
What the codes mean for Turnstile:
| Error Code | Cause |
|---|---|
ERROR_INVALID_TASK_DATA | Missing 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_FORMAT | The proxy is not one clean token. See Proxy Format |
ERROR_BLOCKED_TARGET | websiteURL or your proxy host is, or resolves to, a non-public address |
ERROR_PROXY_CONNECTION_FAILED | TurnstileTask 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_BANNED | TurnstileTask only: the target answered 403 through your proxy, its IP is blocked |
ERROR_TIMEOUT | The solve did not finish in time, or the task expired before a worker picked it up |
ERROR_TASK_QUEUE_FULL | Refused at submission: the queue would make the task wait more than 5 minutes. Nothing was solved, the reservation is refunded |
ERROR_CAPTCHA_UNSOLVABLE | Cloudflare rejected every attempt, or an interactive widget could not be completed |
ERROR_TASK_TYPE_INACTIVE | Your 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.
-
Match the page URL - use the exact page that renders the widget. The site analysis is cached per page and sitekey.
-
Pass the same
actionandcDataas the page - sites that check them insiteverifyreject a token with a different value. -
Prefer a residential proxy for
TurnstileTask- a datacenter address is sent to the interactive stage, which is slower. -
Validate your proxy string - one token, no spaces: see Proxy Format. A whole line copied from a proxy list is refused.