Se o CaptchaAI devolve um token do Turnstile mas a página continua recusando o envio, o problema quase nunca está no serviço de resolução. Ele está em três coisas que você controla: a sitekey que capturou, o pageurl que enviou e o campo onde injetou o token. Acerte esses três pontos e a maioria das falhas do Turnstile desaparece.
O CaptchaAI resolve o Turnstile com alta taxa de sucesso em menos de 10 segundos. Portanto, quando a integração quebra, vale começar a investigação pelos parâmetros da requisição e pela forma como você aplica o token retornado — não pelo serviço.
Um exemplo comum: um time de QA em São Paulo testa o formulário de login em staging.example.com, o envio à API funciona, o token volta, mas a página recarrega sem entrar. Na quase totalidade desses casos, o culpado é o pageurl levemente diferente ou o token indo para o campo errado. Este guia percorre cada etapa onde isso acontece.
Antes de tudo: é o widget Turnstile ou o desafio de página inteira?
Muitas integrações falham porque tratam dois produtos diferentes da Cloudflare como se fossem um só. Vale confirmar qual deles está na sua frente antes de depurar qualquer código de erro, porque o método e a integração mudam por completo:
| Sinal | Widget Turnstile | Desafio de página inteira |
|---|---|---|
| O que você vê | Widget embutido na página (checkbox ou invisível) | Tela de verificação da Cloudflare ocupando a página toda |
| O que a CaptchaAI devolve | Um token para injetar no formulário | Um cookie de validação |
| Método da API | turnstile |
cloudflare_challenge |
| Precisa de proxy? | Opcional | Sim (obrigatório) |
Se o que aparece é uma tela de verificação de página inteira (e não um widget embutido), este guia não é o seu ponto de partida: você precisa do solucionador de desafios de página inteira da Cloudflare, que devolve um cookie de validação e exige um proxy. O restante do artigo trata do widget Turnstile e do token que ele gera.
O que torna o Turnstile diferente
Três características do Turnstile explicam a maioria dos problemas que você não vê em outros tipos de CAPTCHA.
O pageurl precisa ser exato
Os tokens do Turnstile ficam fortemente atrelados ao contexto da página. Nas telas de verificação de página inteira da Cloudflare, usar o URL errado — mesmo que seja apenas um caminho ligeiramente diferente — faz o token ser rejeitado. Não basta o domínio estar certo; o caminho e os parâmetros de consulta também contam.
O token tem dois caminhos de aplicação
O token retornado pode ser aplicado de duas formas, e escolher a errada falha em silêncio:
| Método | Quando usar |
|---|---|
Campo oculto — insira em cf-turnstile-response (e, às vezes, em g-recaptcha-response) |
Quando a página usa um formulário padrão com um input oculto |
Função de callback — chame a função definida em turnstile.render() ou em data-callback |
Quando a página usa validação programática em vez de um formulário |
Os tokens são de uso único
Um token do Turnstile só pode ser verificado uma vez. Se a sua automação o enviar duas vezes por engano, ou se houver uma condição de corrida, a segunda tentativa falha.
Onde os erros do Turnstile acontecem
Antes de olhar códigos de erro, mapeie a etapa. Toda falha do Turnstile cai em uma destas três:
- Etapa de envio — sua requisição a
in.phpé recusada antes mesmo de a tarefa entrar na fila. - Etapa de consulta — a tarefa foi aceita, mas o polling em
res.phpfalha ou estoura o tempo limite. - Etapa de validação — a API devolve um token válido, mas a página de destino o rejeita.
Se você não sabe em qual etapa a falha vive, está adivinhando a correção. Identifique a etapa primeiro; as seções abaixo detalham cada uma.
Erros na etapa de envio
Estes aparecem ao enviar a tarefa para https://ocr.captchaai.com/in.php — a requisição é recusada antes de a tarefa entrar na fila. Localize o código na tabela e aplique a correção:
| Erro | Causa | Correção |
|---|---|---|
ERROR_WRONG_USER_KEY |
Chave de API com formato incorreto (deve ter 32 caracteres) | Confira a chave em captchaai.com/api.php |
ERROR_KEY_DOES_NOT_EXIST |
Chave bem formatada, mas sem conta ativa vinculada | Abra o painel e confirme que a conta está ativa e a chave está correta |
ERROR_ZERO_BALANCE |
Sem threads livres no seu plano | Aguarde a liberação das threads, reduza a simultaneidade ou faça upgrade de plano |
ERROR_PAGEURL |
O parâmetro pageurl está ausente |
Informe o URL completo — protocolo, domínio e caminho (exemplo abaixo) |
ERROR_BAD_PARAMETERS |
Parâmetro obrigatório ausente ou malformado | Confira todos os campos obrigatórios da tabela abaixo |
| Respostas em HTML ou 500/502 | Erro temporário no servidor | Aguarde de 5 a 10 segundos e tente novamente |
No caso do ERROR_PAGEURL, o valor precisa trazer o endereço inteiro, e não apenas o domínio:
pageurl=https://staging.example.com/qa-login
Já o ERROR_BAD_PARAMETERS quase sempre recai sobre um destes campos obrigatórios do Turnstile:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
key |
String | Sim | Sua chave de API da CaptchaAI |
method |
String | Sim | Deve ser turnstile |
sitekey |
String | Sim | A sitekey do widget Turnstile |
pageurl |
String | Sim | URL completo da página |
Estes são opcionais, mas úteis quando a página exige proxy ou uma action específica:
| Parâmetro | Tipo | Descrição |
|---|---|---|
action |
String | Valor de data-action ou do parâmetro action em turnstile.render() |
proxy |
String | Formato: login:password@IP:PORT |
proxytype |
String | HTTP, HTTPS, SOCKS4, SOCKS5 |
Como localizar a sitekey do Turnstile
A sitekey é o parâmetro mais errado com frequência. Há três lugares onde encontrá-la.
Opção 1 — o atributo data-sitekey:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAB1example"></div>
Opção 2 — uma chamada turnstile.render():
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB1example',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
}
});
Opção 3 — interceptar a chamada de renderização (avançado):
Se a sitekey for carregada dinamicamente, redefina turnstile.render antes de o widget inicializar para capturar os 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);
};
Erros na etapa de consulta de resultado
Estes aparecem durante o polling em https://ocr.captchaai.com/res.php. Um aviso primeiro: o CAPCHA_NOT_READY não é um erro — significa apenas que a resolução ainda está em andamento (no Turnstile, costuma levar menos de 10 segundos na CaptchaAI). Os demais códigos, sim, pedem uma ação:
| Código | Causa | Correção |
|---|---|---|
CAPCHA_NOT_READY |
Resolução ainda em andamento (não é erro) | Aguarde 5 segundos e consulte novamente |
ERROR_WRONG_ID_FORMAT |
O ID do CAPTCHA contém caracteres não numéricos | Use o ID exato retornado por in.php, sem nenhuma alteração |
ERROR_WRONG_CAPTCHA_ID |
O ID não corresponde a nenhuma tarefa enviada | Confirme que está consultando o ID que veio na resposta do envio |
ERROR_EMPTY_ACTION |
Falta o parâmetro action na requisição de consulta |
Inclua sempre action=get (veja o formato abaixo) |
ERROR_CAPTCHA_UNSOLVABLE |
Falha na resolução — possível sitekey errada ou página não suportada | Confira a sitekey, refaça a requisição e tente de novo |
ERROR_INTERNAL_SERVER_ERROR |
Problema no servidor | Aguarde 10 segundos e tente novamente |
Uma requisição de consulta bem formada, com action=get e json=1, fica assim:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID&json=1
Observação: use
json=1na consulta para receber respostas no formato{"status": 1, "request": "<token>"}. Sem esse parâmetro, o endpoint devolve texto puro, comoOK|<token>ouCAPCHA_NOT_READY. As duas formas funcionam — escolha a que for mais simples de tratar no seu parser.
Quando a página recusa um token válido
Estas são as falhas mais difíceis de depurar: a API devolve um token com sucesso, mas a página de destino o rejeita. Percorra as quatro causas na ordem abaixo.
Falha 1: token inserido no campo errado
Sintoma: o formulário é enviado, mas a página exibe erro de validação ou apenas recarrega.
As páginas com Turnstile podem esperar o token em campos diferentes:
cf-turnstile-response— o input oculto principal do Turnstileg-recaptcha-response— algumas páginas o usam como alternativa
Correção: inspecione o formulário e preencha ambos os campos, por segurança. Na automação de navegador:
# 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)
Falha 2: callback não disparado
Sintoma: o token está no campo, mas o formulário continua bloqueando o envio.
Causa: a página usa uma função de callback em vez do campo oculto (ou além dele). O callback cuida da lógica extra, como habilitar o botão de envio ou disparar uma requisição AJAX.
Correção: localize e chame o 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
Falha 3: contexto de página incorreto
Sintoma: o token é rejeitado mesmo com a sitekey certa e uma resolução nova.
Causa: o pageurl usado na requisição não corresponde ao contexto real da página. Isso é especialmente comum em:
- Telas de verificação de página inteira da Cloudflare — o URL pode incluir parâmetros de consulta ou trechos de caminho que fazem diferença
- Aplicações de página única (SPAs) — o URL visível pode ser diferente do URL que carregou o widget Turnstile
Correção: abra a aba Network do DevTools e encontre o URL exato de onde o widget Turnstile é carregado. Use esse URL como pageurl.
Falha 4: reutilização de token
Sintoma: a primeira resolução funciona; as seguintes falham.
Causa: os tokens do Turnstile são de uso único. Depois de verificados pelo servidor da Cloudflare, são invalidados.
Correção: peça uma nova resolução para cada envio de formulário. Não guarde em cache nem reutilize tokens.
Referência rápida: erro e correção
| Erro / sintoma | Etapa | Causa provável | Correção |
|---|---|---|---|
ERROR_WRONG_USER_KEY |
Envio | Chave de API malformada | Confira a chave de 32 caracteres |
ERROR_KEY_DOES_NOT_EXIST |
Envio | Chave inválida | Verifique o painel |
ERROR_ZERO_BALANCE |
Envio | Sem threads livres | Aguarde ou faça upgrade do plano |
ERROR_PAGEURL |
Envio | Falta o pageurl |
Informe o URL completo |
ERROR_BAD_PARAMETERS |
Envio | Falta sitekey, method ou pageurl | Confira todos os campos obrigatórios |
CAPCHA_NOT_READY |
Consulta | Resolução em andamento | Aguarde 5 segundos e tente de novo |
ERROR_WRONG_ID_FORMAT |
Consulta | ID do CAPTCHA não numérico | Use o ID exato de in.php |
ERROR_WRONG_CAPTCHA_ID |
Consulta | ID do CAPTCHA inválido | Confira o ID do envio |
ERROR_EMPTY_ACTION |
Consulta | Falta action=get |
Adicione o parâmetro action |
| Token rejeitado pela página | Validação | Campo errado, callback não disparado, URL errado | Confira o nome do campo, chame o callback, valide o pageurl exato |
| Segunda resolução falha | Validação | Reutilização de token | Peça um token novo a cada envio |
Exemplo completo em Python
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
Exemplo completo em Node.js
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);
Perguntas frequentes
Quanto tempo o CaptchaAI leva para resolver um Turnstile?
Menos de 10 segundos na maior parte dos casos. Por isso o exemplo em Python aguarda 10 segundos antes da primeira consulta e depois faz polling a cada 5 segundos. Se a sua integração estoura o tempo limite bem além disso, suspeite dos parâmetros de envio, não da velocidade de resolução.
Posso reutilizar o mesmo token do Turnstile em mais de um envio?
Não. Os tokens do Turnstile são de uso único: assim que o servidor da Cloudflare os verifica, eles são invalidados. Peça uma nova resolução para cada envio de formulário e nunca guarde tokens em cache.
Preciso de proxy para resolver o Turnstile?
Para widgets Turnstile isolados, o proxy é opcional — basta enviar sitekey e pageurl. Já nas telas de verificação de página inteira ele é obrigatório. Quando quiser usar um, acrescente os parâmetros proxy e proxytype à requisição.
Como diferencio um widget Turnstile de um desafio de página inteira?
Se há um formulário com uma caixinha de verificação (ou nada visível, no modo invisível), é o widget Turnstile e você recebe um token. Se a página inteira é substituída por uma tela de verificação da Cloudflare, é o desafio de página inteira, que devolve um cookie e exige proxy.
O CaptchaAI funciona com hCaptcha ou FunCaptcha?
Não. O hCaptcha e o FunCaptcha (Arkose Labs) não são suportados no momento. A CaptchaAI cobre reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3, CAPTCHAs de imagem/OCR e desafios de grade de imagens, além de CaptchaFox (beta), Friendly Captcha (beta) e Lemin (beta).
Corrija o seu fluxo do Turnstile
Se a integração do seu Turnstile está falhando, siga esta lista antes de qualquer outra coisa:
- Confira a sitekey — extraia de
data-sitekeyou deturnstile.render(). - Confira o pageurl — use o URL exato, com protocolo e caminho.
- Confira o caminho do token — a página espera
cf-turnstile-response,g-recaptcha-responseou um callback? - Use
json=1— sempre consulte os resultados do Turnstile em JSON. - Não reutilize tokens — peça uma resolução nova a cada envio.
Uma nota de contexto para times no Brasil e em Portugal: ao registrar logs de requisição em ambientes de QA, evite gravar o token completo ou dados pessoais do formulário — é uma boa prática de conformidade com a LGPD (ou o RGPD, em Portugal) e não atrapalha em nada a depuração.
Comece pelo solucionador de Turnstile da CaptchaAI, valide seus parâmetros na documentação da API e, se precisar entender a mecânica do widget, leia como o Cloudflare Turnstile funciona.
Resumo dos recursos visuais
Imagem principal
- Texto alternativo: desenvolvedor depurando erros do Cloudflare Turnstile — fluxo de envio, requisição controlada ao endpoint de QA e falhas de validação
- Deve mostrar: o fluxo de diagnóstico com as etapas de erro e os caminhos de correção
- Nome do arquivo: cloudflare-turnstile-errors-troubleshooting-hero.png
Visual 1 no artigo
- Posicionamento: após "Erros na etapa de consulta de resultado"
- Tipo: árvore de decisão
- Texto alternativo: árvore de decisão para falhas do Cloudflare Turnstile — erros de envio, erros de consulta e rejeição pela página
- Nome do arquivo: cloudflare-turnstile-error-decision-tree.png
Visual 2 no artigo
- Posicionamento: após "Quando a página recusa um token válido"
- Tipo: diagrama de causas e correções
- Texto alternativo: diagrama mostrando por que os tokens do Turnstile são rejeitados e a correção de cada causa
- Nome do arquivo: cloudflare-turnstile-validation-causes-fixes.png