Para resolver o Cloudflare Turnstile pela API: extraia o sitekey (começa com 0x), envie-o com a URL da página ao endpoint /in.php, faça polling em /res.php e injete o token no campo oculto cf-turnstile-response. É esse fluxo, em quatro passos, que este guia detalha com código pronto em Python e Node.js.
Diferente dos CAPTCHAs tradicionais, o Turnstile raramente mostra desafio visível: coleta sinais do navegador em segundo plano e emite um token que o backend valida. Ainda sem conta? Veja o Quickstart da CaptchaAI.
Os quatro passos, em resumo:
- Extraia o sitekey (
0x...) do HTML ou do script que inicializa o widget. - Envie sitekey e pageurl para o endpoint
/in.php. - Faça polling em
/res.phpatéstatusretornar1. - Injete o token no campo
cf-turnstile-responsee envie o formulário.
Requisitos
| Item | Valor |
|---|---|
| API key da CaptchaAI | No painel em captchaai.com |
| Sitekey do Turnstile | Extraído da página (começa com 0x) |
| URL da página | URL completa onde o Turnstile aparece |
| Linguagem | Python 3.7+ ou Node.js 14+ |
Com esses quatro itens em mãos, siga os passos abaixo na ordem — cada um depende do resultado do anterior.
Passo 1: encontre o sitekey
O sitekey do widget aparece no HTML da página, normalmente dentro de uma div ou de um bloco <script>:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
Ou renderizado por JavaScript:
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
Três formas de extrair:
- DevTools do navegador — aba Elements, busque
data-sitekeyoucf-turnstile. - Código-fonte —
Ctrl+Ue procure strings que comecem com0x. - Aba Network — filtre por
challenges.cloudflare.com; o sitekey vai nos parâmetros da requisição.
O sitekey do Turnstile sempre começa com
0xe geralmente tem 22 caracteres. Isso o diferencia das chaves de reCAPTCHA, que começam com6L.
Passo 2: envie a tarefa
Envie um POST para https://ocr.captchaai.com/in.php com method=turnstile, o sitekey e a URL da página:
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)
O mesmo fluxo em 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;
Uma resposta bem-sucedida devolve {"status": 1, "request": "<task_id>"}. Guarde esse task_id — ele é a referência para o polling no próximo passo.
Passo 3: faça polling do resultado
O Turnstile costuma resolver em 10–25 segundos. Aguarde 10 segundos antes da primeira consulta e repita a cada 5 segundos, até 40 vezes:
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 primeiros caracteres):", token[:60])
Rodando workers em sa-east-1 (São Paulo), o RTT até a CaptchaAI cresce um pouco, mas raramente sai da faixa de 10–25 s. E se o pipeline loga pageurl e dados de usuário, trate isso como dado pessoal sob a LGPD.
O token retornado é uma string Base64 que normalmente começa com 0. e tem entre 400 e 600 caracteres.
Passo 4: injete o token na página
Com o token em mãos, insira-o no campo oculto cf-turnstile-response do formulário e envie normalmente.
Selenium:
driver.execute_script(
"document.querySelector('[name=cf-turnstile-response]').value = arguments[0];",
token,
)
driver.find_element("css selector", "form").submit()
Playwright:
page.evaluate(
"(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
token,
)
page.click("button[type=submit]")
HTTP puro: adicione cf-turnstile-response=<token> no corpo application/x-www-form-urlencoded.
O token do Turnstile vale por 120–300 segundos. Envie-o assim que possível — depois desse intervalo o backend responde
timeout-or-duplicate.
Exemplo completo em Python
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"))
Erros comuns
| Código | Significado | Ação |
|---|---|---|
ERROR_WRONG_USER_KEY |
Formato de API key inválido | Verifique se CAPTCHAAI_KEY está completo |
ERROR_KEY_DOES_NOT_EXIST |
API key não encontrada | Copie novamente do painel |
ERROR_ZERO_BALANCE |
Saldo zero | Recarregue e tente de novo |
ERROR_PAGEURL |
Falta o pageurl | Envie a URL completa com https:// |
ERROR_CAPTCHA_UNSOLVABLE |
Falhou após várias tentativas | Verifique se sitekey e pageurl batem; tente uma vez mais |
Mais detalhes no guia de reCAPTCHA v2.
Quando não funciona
Sitekey dinâmico
Alguns sites com Cloudflare emitem um sitekey novo a cada visita ou a cada deploy do widget. Se o sitekey que funcionava ontem parou de funcionar hoje, esse costuma ser o motivo — reextraia o sitekey imediatamente antes de montar cada tarefa, nunca a partir de um valor salvo em cache.
pageurl fora do padrão esperado
O backend do Turnstile compara a pageurl de forma estrita. Envie o caminho exato onde o widget é renderizado, sem parâmetros de query extras, sem barra final divergente da página real e sem misturar http com https.
Assinatura TLS do cliente
A Cloudflare também observa a assinatura TLS da própria conexão, não só o token do Turnstile, para identificar clientes automatizados. Bibliotecas HTTP genéricas (requests, axios puro) têm uma assinatura diferente de um navegador real. Use curl_cffi, Playwright ou um navegador real para a requisição que carrega a página com o widget.
Token expirado antes do envio
O token vale entre 120 e 300 segundos após emitido. Se o seu pipeline tem uma fila entre "resolver" e "enviar o formulário", meça esse intervalo — se ultrapassar o limite, o desafio precisa ser resolvido de novo.
Qualidade do proxy
IPs de datacenter baratos, sobretudo os já reconhecidos por outros bots, disparam desafios extras com mais frequência. Prefira egress de rede autorizado, com reputação de IP estável e histórico limpo — troque a faixa de IP se a taxa de desafios subir de forma consistente.
Perguntas frequentes
Quanto custa resolver Turnstile com a CaptchaAI?
Cobrança por thread simultânea, não por captcha. O BASIC (US$ 15/mês, 5 threads) já inclui solves ilimitados; veja a tabela completa em captchaai.com/pricing.
O CaptchaAI resolve Turnstile invisível, sem widget na tela?
Sim, e o processo é o mesmo: você continua precisando do sitekey (extraído do HTML ou do script de inicialização) e da pageurl exata.
Existe SDK oficial ou preciso montar as requisições manualmente?
Só a API REST — não há SDK para Turnstile em Python ou Node.js. Encapsule requests/axios em uma função própria, como no exemplo completo acima.
Quanto tempo leva para resolver um Turnstile?
Entre 10 e 25 segundos na maioria dos casos, medido do envio da tarefa até a resposta com o token pronto. A cobrança é por thread simultânea, então rodar várias tarefas em paralelo não aumenta o tempo de cada uma individualmente.
O token funciona em qualquer subdomínio do site?
Não necessariamente. O token é emitido para a pageurl enviada na tarefa; se o formulário de destino está em outro subdomínio ou caminho, reenvie a tarefa com a pageurl correta antes de tentar validar o token nesse endereço.
Próximos passos
Para entender o que acontece por trás do widget antes de automatizar a resolução, veja o material abaixo: