Solución de Problemas

Errores y solución de problemas de Cloudflare Turnstile

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:

  1. Envío — la API rechaza tu tarea al llamar a in.php. Casi siempre es la clave API, el sitekey o el pageurl.
  2. Sondeo — la tarea se acepta, pero al consultar el resultado en res.php falla o se agota el tiempo de espera.
  3. 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 pageurl forma 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 Tu clave API de CaptchaAI
method string Debe ser turnstile
sitekey string Sitekey del widget de Turnstile
pageurl string 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=1 en la solicitud de sondeo. La respuesta JSON puede incluir el user_agent del 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 Turnstile
  • g-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:

  1. Verifica el sitekey — extráelo de data-sitekey o de turnstile.render()
  2. Verifica el pageurl — usa la URL exacta, con protocolo y ruta
  3. Revisa la ruta del token — ¿la página usa cf-turnstile-response, g-recaptcha-response o un callback?
  4. Usa json=1 — activa siempre las respuestas JSON al sondear resultados de Turnstile
  5. 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

Los comentarios están deshabilitados para este artículo.