Para resolver Cloudflare Turnstile no necesitas mantener abierto un navegador completo ni pelearte con el widget en pantalla: bastan tres llamadas HTTP. Envías el sitekey y la URL a la API de CaptchaAI, sondeas hasta que devuelve el token y lo inyectas en el formulario antes de enviarlo. Esta guía recorre ese flujo de principio a fin, con código que puedes pegar directamente.
Turnstile es la alternativa de Cloudflare a los CAPTCHA clásicos: corre en segundo plano, casi nunca muestra un desafío visible y, en lugar de pedirte que selecciones semáforos, recopila señales del navegador y emite un token que el backend valida. Para tu automatización eso significa una sola cosa: tienes que producir ese token. Si es tu primer contacto con la API, el Quickstart de CaptchaAI describe el flujo de cuatro pasos que aquí aplicamos a Turnstile.
Qué necesitas antes de empezar
Con cuatro datos lanzas la primera tarea: el sitekey y la URL identifican el desafío, y la API key autentica tu cuenta.
| Elemento | Valor |
|---|---|
| API key de CaptchaAI | Desde el panel en captchaai.com |
| Sitekey de Turnstile | Extraído de la página (empieza por 0x) |
| URL de la página | URL completa donde aparece Turnstile |
| Lenguaje | Python 3.7+ o Node.js 14+ |
Paso 1: localiza el sitekey de Turnstile
El sitekey vive en el HTML de la página, normalmente dentro de un div o de un script:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
O renderizado por JavaScript:
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
Tienes tres maneras de dar con él. La más rápida es abrir DevTools, ir a Elements y buscar data-sitekey o cf-turnstile. Si prefieres el código fuente, pulsa Ctrl+U y busca cadenas que empiecen por 0x. Y si el widget se carga por JavaScript, filtra la pestaña Network por challenges.cloudflare.com.
El sitekey de Turnstile siempre empieza por
0xy suele tener 22 caracteres. Así lo distingues de las claves de reCAPTCHA, que empiezan por6L.
Paso 2: envía la tarea a CaptchaAI
Haz un POST a https://ocr.captchaai.com/in.php con method=turnstile, el sitekey y la URL:
import requests
API_KEY = "YOUR_CAPTCHAAI_KEY"
SITEKEY = "0x4AAAAAAAC3DHQFLr1GavNl"
PAGEURL = "https://staging.example.com/qa-login"
r = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": SITEKEY,
"pageurl": PAGEURL,
"json": 1,
})
data = r.json()
if data["status"] != 1:
raise RuntimeError(f"submit failed: {data}")
task_id = data["request"]
print("task id:", task_id)
El mismo envío en Node.js:
const axios = require("axios");
const { data } = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: process.env.CAPTCHAAI_KEY,
method: "turnstile",
sitekey: "0x4AAAAAAAC3DHQFLr1GavNl",
pageurl: "https://staging.example.com/qa-login",
json: 1,
},
});
if (data.status !== 1) throw new Error(`submit failed: ${JSON.stringify(data)}`);
const taskId = data.request;
Una respuesta correcta tiene la forma {"status": 1, "request": "<task_id>"}. Guarda ese task_id: es el identificador que usarás para sondear el resultado.
Paso 3: sondea el token
Turnstile suele resolverse en 10–25 segundos, así que no preguntes de inmediato. Espera 10 segundos y luego consulta cada 5, con un tope de 40 iteraciones:
import time
time.sleep(10)
for _ in range(40):
r = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
res = r.json()
if res["status"] == 1:
token = res["request"]
break
if res["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"solver error: {res}")
time.sleep(5)
else:
raise TimeoutError("turnstile solving timed out")
print("token (60 primeros caracteres):", token[:60])
Mientras la tarea sigue en cola, la API devuelve CAPCHA_NOT_READY; no es un error, solo significa "sigue esperando". El token final es una cadena Base64 que suele empezar por 0. y mide entre 400 y 600 caracteres.
Paso 4: inyecta el token y envía el formulario
Con el token en la mano, colócalo en el campo oculto cf-turnstile-response del formulario y envíalo. Con Selenium:
driver.execute_script(
"document.querySelector('[name=cf-turnstile-response]').value = arguments[0];",
token,
)
driver.find_element("css selector", "form").submit()
Con Playwright el patrón es equivalente:
page.evaluate(
"(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
token,
)
page.click("button[type=submit]")
Y sin navegador, con HTTP puro, basta con añadir cf-turnstile-response=<token> al cuerpo application/x-www-form-urlencoded de tu POST.
El token de Turnstile vive entre 120 y 300 segundos. Úsalo justo después de recibirlo; si tardas, el backend responderá
timeout-or-duplicatey tendrás que resolver de nuevo.
Script completo en Python
Aquí tienes las tres etapas encadenadas en una función reutilizable:
import os, time, requests
API = "https://ocr.captchaai.com"
KEY = os.environ["CAPTCHAAI_KEY"]
def solve_turnstile(sitekey: str, pageurl: str) -> str:
r = requests.post(f"{API}/in.php", data={
"key": KEY, "method": "turnstile",
"sitekey": sitekey, "pageurl": pageurl, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] != 1:
raise RuntimeError(f"submit: {j}")
tid = j["request"]
time.sleep(10)
for _ in range(40):
r = requests.get(f"{API}/res.php", params={
"key": KEY, "action": "get", "id": tid, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] == 1:
return j["request"]
if j["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"poll: {j}")
time.sleep(5)
raise TimeoutError("timeout")
if __name__ == "__main__":
print(solve_turnstile("0x4AAAAAAAC3DHQFLr1GavNl", "https://staging.example.com/qa-login"))
Qué revisar cuando Turnstile no se resuelve
Cuando el token no llega o el sitio lo rechaza, casi siempre es una de estas cinco causas:
- Sitekey dinámico. Algunos sitios de Cloudflare generan un sitekey nuevo por visita. Vuelve a extraerlo justo antes de cada tarea, no lo dejes fijo en una constante.
- pageurl inexacto. El backend de Turnstile compara la URL de forma estricta. Envía la ruta exacta, sin query-string.
- Firma TLS del cliente. Cloudflare puede filtrar clientes por su firma TLS. Usa
curl_cffi, Playwright o un navegador real en lugar de un cliente HTTP plano. - Token caducado. Consúmelo en menos de dos minutos o tendrás que resolver otra vez.
- Reputación de la IP. Las IP de datacenter muy baratas disparan desafíos adicionales; una salida de red autorizada y de buena reputación reduce esos reintentos.
Si el fallo llega como código de la API, esta tabla cubre los más habituales:
| Código | Significado | Qué hacer |
|---|---|---|
ERROR_WRONG_USER_KEY |
Formato de API key inválido | Revisa que CAPTCHAAI_KEY esté completo |
ERROR_KEY_DOES_NOT_EXIST |
API key no encontrada | Cópiala de nuevo desde el panel |
ERROR_ZERO_BALANCE |
Saldo cero | Recarga y reintenta |
ERROR_PAGEURL |
Falta el parámetro pageurl | Envía la URL completa con https:// |
ERROR_CAPTCHA_UNSOLVABLE |
Resolución fallida | Comprueba que sitekey y pageurl coincidan; reintenta una vez |
Los parámetros y la mecánica de envío/sondeo son idénticos a los de otros tipos: si vienes de la guía de reCAPTCHA v2 reconocerás el mismo patrón in.php / res.php.
Cuánto cuesta resolver Turnstile a escala
Para un equipo que monitoriza un flujo propio de login o de checkout —el QA nocturno del portal de una agencia en Madrid o Ciudad de México, por ejemplo— importa cuántos Turnstile puede resolver en paralelo y con qué coste fijo. CaptchaAI factura por thread, no por resolución: cada thread es un CAPTCHA en curso y, al terminar, queda libre para el siguiente, sin límites diarios ni recargos por tipo.
El plan BASIC ($15/mes, 5 threads) resuelve hasta 5 Turnstile simultáneos; STANDARD ($30/mes, 15 threads) y ADVANCE ($90/mes, 50 threads) escalan cuando el volumen crece. Para quien factura en monedas locales volátiles, un costo mensual fijo en USD es más fácil de presupuestar que el pago por resolución.
Preguntas frecuentes
¿Puedo resolver Turnstile sin ejecutar un navegador?
Sí. Todo el flujo funciona con simples llamadas HTTP: no necesitas Selenium ni Playwright para conseguir el token, solo para inyectarlo en un formulario ya renderizado. Para un envío por HTTP puro basta con requests o axios.
¿Necesito un proxy para resolver Turnstile?
No en el caso general. A diferencia de las páginas de Cloudflare Challenge, la resolución de Turnstile por API no exige parámetros de proxy. Solo conviene una salida de red de mejor reputación si un sitio concreto te devuelve desafíos extra.
¿Por qué el sitio rechaza el token con timeout-or-duplicate?
Porque el token ya caducó o se reutilizó. Cada token vive entre 120 y 300 segundos y sirve una sola vez: genera uno nuevo y envíalo de inmediato después de recibirlo.
¿Cómo sé que el sitekey que copié es de Turnstile y no de otro proveedor?
El de Turnstile empieza por 0x y ronda los 22 caracteres; el de reCAPTCHA empieza por 6L. Si los confundes, la API devolverá ERROR_CAPTCHA_UNSOLVABLE.
¿Puedo lanzar varias resoluciones de Turnstile a la vez?
Sí. Cada resolución en curso ocupa un thread de tu plan, así que con 5 threads corres 5 tareas en paralelo. Reutiliza la función solve_turnstile desde varios hilos o procesos.
Siguiente paso
- Repasa el Quickstart de CaptchaAI para el flujo de cuatro pasos aplicado a cualquier CAPTCHA.
- Compara con la guía de reCAPTCHA v2, otro caso habitual con el mismo patrón de API.
- Regístrate en captchaai.com, obtén tu API key y resuelve tu primer Turnstile en minutos.