Casi todos los fallos de Cloudflare Turnstile se reducen a tres cosas: un pageurl que no coincide con la página real, un sitekey capturado del elemento equivocado, o un token que aplicas por la ruta incorrecta. Si tu integración falla, el problema rara vez está en el solver: está en lo que envías a la API o en cómo colocas el token que te devuelve.
Turnstile, además, es más estricto que otros captchas en un punto concreto: los tokens quedan atados al contexto exacto de la página, sobre todo en las pantallas de challenge de Cloudflare. Por eso una ruta ligeramente distinta o un parámetro de consulta que falta bastan para que el token se rechace aunque la resolución haya sido correcta.
CaptchaAI resuelve Turnstile con una tasa de éxito alta y constante en menos de 10 segundos. La estrategia para arreglar los fallos rápido es siempre la misma: localiza en qué etapa se rompe la integración antes de tocar nada.
Antes de depurar: ¿es Turnstile o un challenge de Cloudflare?
Muchas horas de depuración se pierden por confundir dos cosas distintas. Turnstile es un widget que se integra en tu formulario y devuelve un token para inyectar; el challenge de Cloudflare es una pantalla de verificación a página completa y se resuelve por otra vía. Identifica cuál tienes delante antes de seguir:
| Señal | Turnstile | Cloudflare Challenge |
|---|---|---|
| Lo que ves | Widget integrado en la página (casilla o invisible) | Pantalla de verificación de Cloudflare a página completa |
| Lo que devuelve CaptchaAI | Un token para inyectar en el formulario | Una cookie qa_validation_cookie |
| Método API | turnstile |
cloudflare_challenge |
| ¿Requiere proxy? | Opcional | Sí (obligatorio) |
Si lo que ves es un challenge a página completa y no un widget, esta guía no es la tuya: necesitas el solver de Cloudflare Challenge, que devuelve una cookie qa_validation_cookie y requiere un proxy. Todo lo que sigue asume que trabajas con el widget de Turnstile.
Localiza primero la etapa del fallo
Antes de mirar códigos de error concretos, ubica el problema en una de estas tres etapas. Cambiar cosas al azar sin saber dónde falla es la forma más lenta de depurar:
- Envío — la API rechaza tu tarea al llamar a
in.php. Casi siempre es la clave API, el sitekey o elpageurl. - Sondeo — la tarea se acepta, pero al consultar el resultado en
res.phpfalla o se agota el tiempo de espera. - Validación en la página — la API devuelve un token válido, pero la página de destino lo rechaza. Aquí entran el campo equivocado, el callback y la URL exacta.
Tres particularidades de Turnstile que explican la mayoría de los rechazos
Turnstile se comporta de forma distinta al resto de captchas en tres aspectos, y casi todos los rechazos «inexplicables» nacen de aquí:
- La URL exacta pesa mucho. Los tokens están atados al contexto de la página. En las pantallas de challenge, una ruta apenas distinta o un parámetro de consulta que falta invalidan el token: el
pageurlforma parte de lo que se firma, no es un dato cosmético. - El token se aplica por dos rutas distintas. Elegir la equivocada falla en silencio, sin mensaje de error (lo detallamos en la tabla siguiente).
- Cada token sirve una sola vez. En cuanto Cloudflare lo verifica, queda invalidado; un envío duplicado o una condición de carrera rompe el segundo intento.
Las dos rutas para aplicar el token
| Método | Cuándo usarlo |
|---|---|
Campo oculto — insertar en cf-turnstile-response (y a veces en g-recaptcha-response) |
Cuando la página usa un formulario estándar con un input oculto |
Callback — llama a la función definida en turnstile.render() o data-callback |
Cuando la página valida de forma programática en lugar de usar un formulario |
¿No sabes cuál usa la página? Revisa el formulario: si existe un input oculto cf-turnstile-response, empieza por el campo oculto; si el botón de envío está deshabilitado hasta resolver el widget, casi seguro hay un callback de por medio.
Errores al enviar la tarea a in.php
Estos aparecen en la respuesta de https://ocr.captchaai.com/in.php. Los más directos se despachan de un vistazo:
| Error | Causa | Solución |
|---|---|---|
ERROR_WRONG_USER_KEY |
El formato de la clave API es incorrecto (debe tener 32 caracteres) | Verifica la clave en captchaai.com/api.php |
ERROR_KEY_DOES_NOT_EXIST |
La clave tiene el formato correcto, pero no está vinculada a una cuenta activa | Revisa tu panel de control: cuenta activa y clave correcta |
ERROR_ZERO_BALANCE |
No hay threads libres en tu plan | Espera a que se liberen, baja la simultaneidad o sube de plan |
| Respuestas HTML o 500/502 | Error transitorio del lado del servidor | Espera de 5 a 10 segundos y reintenta |
Dos errores de envío necesitan algo más de contexto.
ERROR_PAGEURL
Falta el parámetro pageurl. Añade la URL completa —protocolo, dominio y ruta— tal cual:
pageurl=https://staging.example.com/qa-login
ERROR_BAD_PARAMETERS
Faltan parámetros obligatorios o están mal formados. Para Turnstile, estos son los que la API exige:
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
key |
string | Sí | Tu clave API de CaptchaAI |
method |
string | Sí | Debe ser turnstile |
sitekey |
string | Sí | Sitekey del widget de Turnstile |
pageurl |
string | Sí | URL completa de la página |
Y estos son opcionales, pero útiles:
| Parámetro | Tipo | Descripción |
|---|---|---|
action |
string | Valor de data-action o del parámetro action de turnstile.render() |
proxy |
string | Formato: login:password@IP:PORT |
proxytype |
string | HTTP, HTTPS, SOCKS4, SOCKS5 |
Si el error persiste con todos los campos presentes, revisa el tipo de cada uno: un sitekey mal copiado o un pageurl sin protocolo son las causas más habituales.
Dónde encontrar el sitekey de Turnstile
El sitekey es el parámetro que más gente equivoca. Estos son los tres sitios donde localizarlo.
Opción 1 — el atributo data-sitekey:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAB1example"></div>
Opción 2 — una llamada a turnstile.render():
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB1example',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
}
});
Opción 3 — interceptar la llamada de renderizado (avanzado):
Si el sitekey se carga de forma dinámica, puedes redefinir turnstile.render antes de que el widget se inicialice para capturar los parámetros:
// 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);
};
Errores al consultar el resultado en res.php
Estos aparecen al sondear https://ocr.captchaai.com/res.php. Salvo uno, se resuelven rápido:
| Respuesta | Qué significa | Qué hacer |
|---|---|---|
CAPCHA_NOT_READY |
No es un error: la resolución sigue en curso (en CaptchaAI, Turnstile suele tardar menos de 10 segundos) | Espera 5 segundos y vuelve a consultar el resultado |
ERROR_WRONG_ID_FORMAT |
El ID del captcha contiene caracteres no numéricos | Usa el ID exacto que devolvió in.php, sin modificarlo |
ERROR_WRONG_CAPTCHA_ID |
El ID no coincide con ninguna tarea enviada | Confirma que sondeas el ID de la respuesta de envío |
ERROR_CAPTCHA_UNSOLVABLE |
La resolución falló: posible sitekey incorrecto o configuración de página no compatible | Revisa el sitekey, vuelve a enviar la solicitud y reintenta |
ERROR_INTERNAL_SERVER_ERROR |
Problema del lado del servidor | Espera 10 segundos y reintenta |
Solo uno merece detalle aparte.
ERROR_EMPTY_ACTION
Falta el parámetro action en tu solicitud de sondeo. Incluye siempre action=get:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID&json=1
Nota: para Turnstile, usa siempre
json=1en la solicitud de sondeo. La respuesta JSON puede incluir eluser_agentdel solver, que algunas páginas protegidas por Cloudflare exigen para validar el token correctamente.
Cuando la página rechaza un token válido
Estos son los fallos más difíciles de depurar: la API devuelve el token sin problemas, pero la página de destino lo rechaza. No hay un código de error que te oriente, así que conviene descartarlos en orden.
Fallo 1: token insertado en el campo equivocado
Síntoma: el formulario se envía, pero la página devuelve un error de validación o se recarga.
Las páginas con Turnstile pueden esperar el token en campos distintos:
cf-turnstile-response: el input oculto principal de Turnstileg-recaptcha-response: algunas páginas lo usan como alternativa
Solución: revisa el formulario de la página en busca de ambos campos. En automatización de navegador, inyecta el token en los dos por seguridad:
# 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)
Fallo 2: el callback no se dispara
Síntoma: el token está en el campo, pero el formulario sigue bloqueando el envío.
Causa: la página usa una función callback en lugar del campo oculto (o además de él). El callback gestiona lógica extra, como habilitar el botón de envío o lanzar una solicitud AJAX.
Solución: localiza y llama al 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
Fallo 3: contexto de página incorrecto
Síntoma: token rechazado a pesar de un sitekey correcto y una resolución nueva.
Causa: el pageurl que enviaste a la API no coincide con el contexto real de la página. Es especialmente frecuente en:
- Páginas de challenge de Cloudflare: la URL puede incluir parámetros de consulta o componentes de ruta que importan
- Aplicaciones de una sola página (SPA): la URL visible puede diferir de la que cargó el widget de Turnstile
Un caso habitual en equipos de la región: una agencia en Ciudad de México automatiza el QA de un portal de trámites (del tipo cita previa o gestión pública) que carga Turnstile dentro de una SPA. En la barra del navegador se ve /agenda, pero el widget se inicializó en /agenda/paso-2. El equipo enviaba /agenda como pageurl y el token se rechazaba una y otra vez. La solución no fue tocar el código de resolución, sino corregir el pageurl.
Solución: usa la pestaña Network de DevTools para encontrar la URL exacta desde la que se carga el widget de Turnstile. Usa esa URL como pageurl.
Fallo 4: reutilización del token
Síntoma: la primera resolución funciona, las siguientes fallan.
Causa: los tokens de Turnstile son de un solo uso. Una vez que el servidor de Cloudflare los verifica, quedan invalidados.
Solución: solicita una resolución nueva para cada envío de formulario. No guardes en caché ni reutilices tokens.
Python: resolución completa de Turnstile
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "0x4AAAAAAAB1example"
PAGE_URL = "https://staging.example.com/qa-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: resolución completa de Turnstile
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "0x4AAAAAAAB1example";
const PAGE_URL = "https://staging.example.com/qa-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);
Preguntas frecuentes
¿Cómo sé si el fallo está en el envío, en el sondeo o en la validación de la página?
Míralo por el punto donde se rompe. Si el error llega en la respuesta de in.php, es la etapa de envío (clave, sitekey o pageurl). Si res.php devuelve un error o nunca resuelve, es sondeo. Si obtienes un token pero la página lo rechaza, es validación.
¿Sirve el mismo sitekey en staging que en producción?
Casi nunca. Cada entorno suele tener su propio widget con un sitekey distinto. Extrae el sitekey de la página exacta que vas a resolver, no lo reutilices entre entornos.
¿Necesito enviar el parámetro action al resolver Turnstile?
Solo si la página lo usa. Si el widget se inicializa con un valor de action (en data-action o en turnstile.render()), envía ese mismo valor; si lo omites cuando la página lo espera, el token puede quedar fuera de contexto.
¿Cuánto tarda CaptchaAI en resolver un Turnstile?
Normalmente menos de 10 segundos. Por eso el código de ejemplo espera 10 segundos antes del primer sondeo y luego consulta cada 5 segundos hasta recibir el token.
¿Puedo reutilizar un token de Turnstile para varios envíos?
No. Cada token es de un solo uso: en cuanto Cloudflare lo verifica, queda invalidado. Pide una resolución nueva por cada envío de formulario.
Arregla tu integración de Turnstile
Si tu integración de Turnstile falla, recorre esta lista en orden:
- Verifica el sitekey — extráelo de
data-sitekeyo deturnstile.render() - Verifica el pageurl — usa la URL exacta, con protocolo y ruta
- Revisa la ruta del token — ¿la página usa
cf-turnstile-response,g-recaptcha-responseo un callback? - Usa
json=1— activa siempre las respuestas JSON al sondear resultados de Turnstile - No reutilices tokens — solicita una resolución nueva por cada envío
Empieza con el solver de Turnstile de CaptchaAI, contrasta tus parámetros con la documentación de la API y lee cómo funciona Cloudflare Turnstile si necesitas repasar la mecánica del widget. Con un plan por threads como BASIC ($15/mes, 5 threads) tienes resoluciones ilimitadas por thread y un costo mensual fijo en USD, útil si trabajas por volumen.
Registro de iteración
| Iteración | Enfoque | Cambios |
|---|---|---|
| Borrador 1 | Estructura y contenido | Borrador inicial de solución de problemas: 3 etapas de error, tabla de error a solución y preguntas frecuentes |
| Borrador 2 | Precisión técnica | Códigos de error, parámetros de Turnstile y rutas de token verificados con captchaai.com/api-docs. Se añadieron tablas de parámetros. Confirmado method=turnstile y ambos campos cf-turnstile-response / g-recaptcha-response. |
| Borrador 3 | Código y profundidad de inyección | Se añadieron ejemplos de resolución en Python y Node.js. Se añadieron los tres métodos de extracción de sitekey. Se añadió el código de inyección con Selenium para ambos campos de token y la detección de callback. |
| Borrador 4 | Contenido de diferenciación | Se añadió la tabla comparativa de Turnstile y Cloudflare Challenge, la nota de json=1 para el user_agent y la técnica de interceptación de renderizado para sitekeys dinámicos. |
| Borrador 5 | Pulido final de QA | Todos los códigos de error verificados contra los documentos oficiales. Se añadió la tabla de referencia rápida. Introducción ajustada. Se añadió la advertencia de token de un solo uso. Enlaces cruzados confirmados con los artículos del grupo. |
Resumen de activos visuales
Imagen de héroe
- Texto alternativo: el desarrollador soluciona errores de Cloudflare Turnstile: flujo de solicitud, inyección de token y fallos de validación
- Debe mostrar: flujo de solución de problemas con etapas de error y rutas de solución
- Nombre de archivo: cloudflare-turnstile-errors-troubleshooting-hero.png
Visual 1 en el artículo
- Ubicación: después de "Errores al consultar el resultado en
res.php" - Tipo: árbol de decisión
- Texto alternativo: árbol de decisión para fallos de Cloudflare Turnstile: errores de envío frente a errores de sondeo frente a rechazo de la página
- Nombre de archivo: cloudflare-turnstile-error-decision-tree.png
Visual 2 en el artículo
- Ubicación: después de "Cuando la página rechaza un token válido"
- Tipo: diagrama de causas y soluciones
- Texto alternativo: diagrama que muestra por qué se rechazan los tokens de Turnstile y la solución para cada causa
- Nombre de archivo: cloudflare-turnstile-validation-causes-fixes.png
Artículos relacionados
- Cloudflare Challenge frente a Turnstile: cómo distinguirlos
- Cloudflare Turnstile devuelve 403 tras el token: solución
- Cómo resolver Cloudflare Turnstile con la API