Most Cloudflare Turnstile failures are not random. They fall into three categories: request-stage errors (your submission to the API is rejected), result-stage errors (polling fails or times out), and target-page validation failures (the API returns a valid token but the page still rejects it).
The three biggest Turnstile-specific problems are:
- Wrong exact page URL — especially on Cloudflare challenge pages, where context is stricter
- Wrong sitekey — captured from the wrong element or a different widget instance
- Token applied through the wrong path — the page expects
cf-turnstile-response, a callback, or both
CaptchaAI solves Turnstile with a 100% success rate in under 10 seconds. When your integration fails, the problem is almost always in the parameters you send or how you apply the returned token.
Turnstile-specific failure points
Before diving into error codes, understand the three things that make Turnstile different from other captcha types:
1. Exact page URL matters more
Turnstile tokens are tightly bound to the page context. On Cloudflare challenge pages (the full-page verification screen), using the wrong URL — even a slightly different path — will cause the token to be rejected.
2. Two token application paths
The returned token can be applied in two ways, and using the wrong one will fail silently:
| Method | When to use |
|---|---|
Hidden field — insert into cf-turnstile-response (and sometimes g-recaptcha-response) |
When the page uses a standard form with a hidden input |
Callback function — call the function defined in turnstile.render() or data-callback |
When the page uses programmatic validation instead of a form |
3. Tokens are single-use
A Turnstile token can only be verified once. If your automation accidentally submits it twice, or if there is a race condition, the second attempt will fail.
Request-stage errors
These happen when submitting the task to https://ocr.captchaai.com/in.php.
ERROR_WRONG_USER_KEY
Cause: API key format is incorrect (should be 32 characters).
Fix: Verify the key from captchaai.com/api.php.
ERROR_KEY_DOES_NOT_EXIST
Cause: The key is correctly formatted but not linked to an active account.
Fix: Check your dashboard. Make sure the account is active and the key is correct.
ERROR_ZERO_BALANCE
Cause: No free threads on your plan.
Fix: Wait for threads to free up, lower concurrency, or upgrade.
ERROR_PAGEURL
Cause: The pageurl parameter is missing.
Fix: Add the full URL — protocol, domain, and path:
pageurl=https://example.com/login
ERROR_BAD_PARAMETERS
Cause: Required parameters are missing or malformed. For Turnstile, the required parameters are:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
String | Yes | Your CaptchaAI API key |
method |
String | Yes | Must be turnstile |
sitekey |
String | Yes | Turnstile widget sitekey |
pageurl |
String | Yes | Full page URL |
Optional but useful:
| Parameter | Type | Description |
|---|---|---|
action |
String | Value of data-action or the action parameter from turnstile.render() |
proxy |
String | Format: login:password@IP:PORT |
proxytype |
String | HTTP, HTTPS, SOCKS4, SOCKS5 |
Fix: Verify all required fields are present and correctly typed.
HTML or 500/502 responses
Cause: Transient server-side error.
Fix: Wait 5–10 seconds and retry.
How to find the Turnstile sitekey
The sitekey is the most commonly wrong parameter. Here is where to find it:
Option 1 — the data-sitekey attribute:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAB1example"></div>
Option 2 — a turnstile.render() call:
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB1example',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
}
});
Option 3 — intercepting the render call (advanced):
If the sitekey is dynamically loaded, you can redefine turnstile.render before the widget initializes to capture the parameters:
// Inject this before the Turnstile script loads
const originalRender = window.turnstile.render;
window.turnstile.render = function(container, params) {
console.log('Sitekey:', params.sitekey);
console.log('Action:', params.action);
return originalRender.call(this, container, params);
};
Result-stage errors
These happen when polling https://ocr.captchaai.com/res.php.
CAPCHA_NOT_READY
Not an error. The solve is still in progress. Turnstile solves at CaptchaAI typically take under 10 seconds.
Fix: Wait 5 seconds and poll again.
ERROR_WRONG_ID_FORMAT
Cause: The captcha ID contains non-numeric characters.
Fix: Use the exact ID returned by in.php, unmodified.
ERROR_WRONG_CAPTCHA_ID
Cause: The ID does not match any submitted task.
Fix: Verify you are polling the correct ID from the submission response.
ERROR_EMPTY_ACTION
Cause: The action parameter is missing in your poll request.
Fix: Always include action=get:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID&json=1
Note: For Turnstile, always use
json=1in your poll request. The JSON response may include the solver'suser_agent, which some Cloudflare-protected pages require for successful token validation.
ERROR_CAPTCHA_UNSOLVABLE
Cause: The solve failed — possibly wrong sitekey or an unsupported page configuration.
Fix: Verify the sitekey, refresh the request, and retry.
ERROR_INTERNAL_SERVER_ERROR
Cause: Server-side issue.
Fix: Wait 10 seconds and retry.
Target-page validation failures
These are the hardest to debug because the API returns a token successfully, but the target page rejects it.
Failure 1: Token inserted into the wrong field
Symptom: Form submits but the page returns a validation error or refreshes.
Turnstile pages can expect the token in different fields:
cf-turnstile-response— the primary Turnstile hidden inputg-recaptcha-response— some pages use this as a fallback
Fix: Check the page's form for both fields. In browser automation:
# Selenium — inject into both fields for safety
driver.execute_script("""
var cfField = document.querySelector('[name="cf-turnstile-response"]');
var gField = document.querySelector('[name="g-recaptcha-response"]');
if (cfField) cfField.value = arguments[0];
if (gField) gField.value = arguments[0];
""", token)
Failure 2: Callback not triggered
Symptom: Token is in the field, but the form still blocks submission.
Cause: The page uses a callback function instead of (or in addition to) the hidden field. The callback handles additional logic like enabling the submit button or sending an AJAX request.
Fix: Find and call the callback:
// Check data-callback attribute
const callbackName = document.querySelector('.cf-turnstile').getAttribute('data-callback');
if (callbackName && window[callbackName]) {
window[callbackName](token);
}
// Or if it was passed in turnstile.render()
// You may need to intercept the render call to capture it
Failure 3: Wrong exact page context
Symptom: Token rejected despite correct sitekey and fresh solve.
Cause: The pageurl used in the API request does not match the page's actual context. This is especially common on:
- Cloudflare challenge pages — the URL may include query parameters or path components that matter
- Single-page applications — the visible URL may differ from the URL that loaded the Turnstile widget
Fix: Use DevTools Network tab to find the exact URL that the Turnstile widget loads from. Use that URL as pageurl.
Failure 4: Token reuse
Symptom: First solve works, subsequent ones fail.
Cause: Turnstile tokens are single-use. Once verified by Cloudflare's server, the token is invalidated.
Fix: Request a new solve for each form submission. Do not cache or reuse tokens.
Quick error-to-fix reference
| Error / Symptom | Stage | Likely cause | Fix |
|---|---|---|---|
ERROR_WRONG_USER_KEY |
Submit | Malformed API key | Verify 32-char key |
ERROR_KEY_DOES_NOT_EXIST |
Submit | Invalid key | Check dashboard |
ERROR_ZERO_BALANCE |
Submit | No free threads | Wait or upgrade plan |
ERROR_PAGEURL |
Submit | Missing pageurl |
Add full URL |
ERROR_BAD_PARAMETERS |
Submit | Missing sitekey, method, or pageurl | Verify all required fields |
CAPCHA_NOT_READY |
Poll | Solve in progress | Wait 5 seconds, retry |
ERROR_WRONG_ID_FORMAT |
Poll | Non-numeric captcha ID | Use exact ID from in.php |
ERROR_WRONG_CAPTCHA_ID |
Poll | Invalid captcha ID | Verify submission ID |
ERROR_EMPTY_ACTION |
Poll | Missing action=get |
Add action parameter |
| Token rejected by page | Validation | Wrong field, callback not triggered, wrong URL | Check field name, call callback, verify exact pageurl |
| Second solve fails | Validation | Token reuse | Request fresh token per submission |
Python: complete Turnstile solve
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "0x4AAAAAAAB1example"
PAGE_URL = "https://example.com/login"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_turnstile(api_key, sitekey, pageurl):
"""Submit a Turnstile challenge and return the solved token."""
# Submit
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": pageurl,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Wait before first poll (Turnstile is fast — 10 seconds is usually enough)
time.sleep(10)
# Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return result_data["request"]
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("Turnstile solve timed out")
# Usage
token = solve_turnstile(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")
# Inject into cf-turnstile-response and/or g-recaptcha-response
# Then submit the form
Node.js: complete Turnstile solve
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "0x4AAAAAAAB1example";
const PAGE_URL = "https://example.com/login";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveTurnstile(apiKey, sitekey, pageurl) {
// Submit
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "turnstile",
sitekey: sitekey,
pageurl: pageurl,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
// Turnstile is fast — wait 10 seconds before first poll
await sleep(10_000);
// Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return resultData.request;
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("Turnstile solve timed out");
}
// Usage
solveTurnstile(API_KEY, SITEKEY, PAGE_URL)
.then((token) => {
console.log(`Solved token: ${token.slice(0, 80)}...`);
// Inject into cf-turnstile-response and/or g-recaptcha-response
})
.catch(console.error);
Turnstile vs Cloudflare Challenge: which are you facing?
| Signal | Turnstile | Cloudflare Challenge |
|---|---|---|
| What you see | Embedded widget on the page (checkbox or invisible) | Full-page Cloudflare verification screen |
| What CaptchaAI returns | A token to inject into the form | A cf_clearance cookie |
| API method | turnstile |
cloudflare_challenge |
| Proxy required? | Optional | Yes (required) |
If you are hitting a full-page Cloudflare challenge (not an embedded widget), you need the Cloudflare Challenge Solver instead, which returns a cf_clearance cookie and requires a proxy.
FAQ
Why does Turnstile fail even when the request looks correct?
The three most common causes: (1) the pageurl is slightly wrong — especially on Cloudflare challenge pages, (2) the sitekey was captured from the wrong element, or (3) the token is being injected into the wrong field or callback path.
What is the most common Turnstile mistake?
Using the wrong exact page URL. Turnstile tokens are tightly bound to the page context — even a different path or missing query parameter can cause rejection.
What does CAPCHA_NOT_READY mean?
The solve is still in progress. Wait 5 seconds and poll again. Turnstile solves typically complete in under 10 seconds.
What should I do if the API returns a token but the page still rejects it?
- Verify the
pageurlmatches the exact page where the widget loads. - Check whether the page expects the token in
cf-turnstile-response,g-recaptcha-response, or both. - Check if the page uses a callback function instead of (or in addition to) a hidden field.
- Make sure the token is used only once — Turnstile tokens are single-use.
Does CaptchaAI support proxies for Turnstile?
Yes. Add proxy and proxytype to your request. Proxies are optional for standalone Turnstile widgets but recommended on Cloudflare challenge pages.
What is the difference between Turnstile and Cloudflare Challenge?
Turnstile is an embedded widget that returns a token. Cloudflare Challenge is a full-page verification that returns a cf_clearance cookie. They use different API methods and different integration patterns, though both are Cloudflare products.
Fix your Turnstile workflow
If your Turnstile integration is failing:
- Verify the sitekey — Extract it from
data-sitekeyorturnstile.render() - Verify the pageurl — Use the exact URL, including protocol and path
- Check the token path — Does the page use
cf-turnstile-response,g-recaptcha-response, or a callback? - Use
json=1— Always use JSON responses when polling Turnstile results - Do not reuse tokens — Request a fresh solve for each submission
Start with the CaptchaAI Turnstile solver, verify your parameters against the API docs, and read How Cloudflare Turnstile Works if you need background on the widget mechanics.
Iteration log
| Iteration | Focus | Changes |
|---|---|---|
| Draft 1 | Structure and content | Initial troubleshooting draft — 3 error stages, error-to-fix table, FAQ |
| Draft 2 | Technical accuracy | Verified error codes, Turnstile parameters, and token paths against captchaai.com/api-docs. Added parameter tables. Confirmed method=turnstile and both cf-turnstile-response / g-recaptcha-response fields. |
| Draft 3 | Code and injection depth | Added Python and Node.js solve examples. Added sitekey extraction methods (3 approaches). Added Selenium injection code for both token fields. Added callback detection code. |
| Draft 4 | Differentiation content | Added Turnstile vs Cloudflare Challenge comparison table. Added json=1 requirement note for user_agent. Added render interception technique for dynamic sitekeys. |
| Draft 5 | Final QA polish | Verified all error codes match official docs. Added quick-reference table. Tightened intro. Added single-use token warning. Confirmed cross-links to cluster articles. FAQ answers are schema-ready. |
Visual asset brief
Hero image
- Alt text: Developer troubleshooting Cloudflare Turnstile errors — request flow, token injection, and validation failures
- Must show: Technical troubleshooting flow with error stages and fix paths
- File name: cloudflare-turnstile-errors-troubleshooting-hero.png
In-article visual 1
- Placement: After "Result-stage errors"
- Type: Decision tree
- Alt text: Decision tree for Cloudflare Turnstile failures — request errors vs polling errors vs page rejection
- File name: cloudflare-turnstile-error-decision-tree.png
In-article visual 2
- Placement: After "Target-page validation failures"
- Type: Causes-and-fixes diagram
- Alt text: Diagram showing why Turnstile tokens get rejected and the fix for each cause
- File name: cloudflare-turnstile-validation-causes-fixes.png
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.