Se o formulário "trava" depois que você injeta o token — sem erro, sem redirecionamento, só silêncio — o problema quase nunca é o token em si. Na maioria dos casos é o callback que nunca foi disparado: o reCAPTCHA Invisible depende de uma função JavaScript para de fato submeter o formulário, e injetar o token sozinho não basta.
Esse é só um dos quatro erros que respondem pela maior parte das falhas em automação com reCAPTCHA Invisible. Os outros três: o widget não é detectado porque não existe caixa de seleção visível para procurar; o parâmetro invisible=1 fica de fora da requisição à CaptchaAI; e o token expira porque foi resolvido cedo demais e ficou parado na fila até o envio. Este guia cobre os sete padrões de erro mais comuns, com a causa exata e a correção pronta para aplicar. Se você ainda não conhece o mecanismo por trás do widget invisível, comece por Como funciona o reCAPTCHA Invisible e como resolvê-lo.
Tabela rápida: erro, causa e correção
| Erro | Causa | Correção |
|---|---|---|
ERROR_WRONG_GOOGLEKEY |
Sitekey errada ou de outro domínio | Extraia a sitekey da div do widget invisível ou da chamada grecaptcha.render() |
ERROR_PAGEURL |
URL trocada — foi enviada a URL da página pai em vez da URL onde o widget carrega | Use exatamente a URL onde o widget invisível aparece |
ERROR_CAPTCHA_UNSOLVABLE |
O Google marcou a tarefa como impossível | Repita com proxy e cookies novos; confira se o site migrou para v3 |
ERROR_BAD_TOKEN_OR_PAGEURL |
O site de destino rejeitou o token | Confira se o pageurl bate exatamente; injete via callback, não em campo oculto |
CAPCHA_NOT_READY |
A tarefa ainda está em processamento | Continue consultando a cada 5 s; o reCAPTCHA Invisible normalmente resolve em menos de 30 s |
ERROR_KEY_DOES_NOT_EXIST |
Chave de API da CaptchaAI inválida | Confira a chave em captchaai.com/account |
| Token aceito, mas o formulário não reage | O callback não foi executado depois da injeção | Localize e chame a função de data-callback passando o token |
Erro 1: o CaptchaAI não encontra o widget invisível na página
O reCAPTCHA Invisible não tem caixa de seleção visível — não há nada para o seu scraper "clicar" ou sequer perceber.
Se a detecção falhar, as requisições protegidas pelo captcha caem silenciosamente: sem exceção óbvia, só formulários que nunca são aceitos ou redirecionamentos estranhos.
Como identificar o reCAPTCHA Invisible no HTML
Procure estes três padrões na página:
<!-- Pattern 1: div with data-size="invisible" -->
<div class="g-recaptcha" data-sitekey="6LdKlZEU..."
data-size="invisible"
data-callback="onSubmit"></div>
<!-- Pattern 2: button with data-sitekey and invisible size -->
<button class="g-recaptcha"
data-sitekey="6LdKlZEU..."
data-callback="onSubmit"
data-action="submit">Submit</button>
<!-- Pattern 3: programmatic render with size: invisible -->
<script>
grecaptcha.render('submit-btn', {
sitekey: '6LdKlZEU...',
callback: onSubmit,
size: 'invisible'
});
</script>
Detecção em Python
import requests
from bs4 import BeautifulSoup
import re
def detect_invisible_recaptcha(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Check for data-size="invisible"
widget = soup.find("div", {"data-size": "invisible", "class": "g-recaptcha"})
if widget:
return {
"type": "invisible",
"sitekey": widget.get("data-sitekey"),
"callback": widget.get("data-callback")
}
# Check for programmatic render with invisible
scripts = soup.find_all("script")
for script in scripts:
if script.string and "size" in str(script.string) and "invisible" in str(script.string):
key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
if key_match:
return {
"type": "invisible-programmatic",
"sitekey": key_match.group(1),
"callback": "check grecaptcha.render() call"
}
return None
A mesma checagem em Node.js
const axios = require("axios");
const cheerio = require("cheerio");
async function detectInvisibleRecaptcha(url) {
const { data } = await axios.get(url);
const $ = cheerio.load(data);
// Check for data-size="invisible"
const widget = $(".g-recaptcha[data-size='invisible']");
if (widget.length) {
return {
type: "invisible",
sitekey: widget.attr("data-sitekey"),
callback: widget.attr("data-callback"),
};
}
// Check script tags for programmatic invisible render
const scriptContent = $("script")
.map((_, el) => $(el).html())
.get()
.join("\n");
if (scriptContent.includes("invisible")) {
const keyMatch = scriptContent.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
if (keyMatch) {
return {
type: "invisible-programmatic",
sitekey: keyMatch[1],
callback: "check grecaptcha.render() call",
};
}
}
return null;
}
Erro 2: ERROR_WRONG_GOOGLEKEY — a sitekey não é a do widget invisível
Esse erro aparece quando a sitekey enviada não é a do widget reCAPTCHA Invisible que está de fato na página de destino.
As causas mais comuns: a sitekey foi copiada de uma caixa de seleção v2 padrão em outra página do mesmo site; a sitekey veio da URL âncora de uma versão diferente do reCAPTCHA; ou a página tem mais de um widget reCAPTCHA e o scraper pegou o widget errado.
Como extrair a sitekey certa
import requests
from bs4 import BeautifulSoup
def get_invisible_sitekey(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# Priority 1: invisible widget
widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
if widget:
return widget["data-sitekey"]
# Priority 2: any g-recaptcha div (may be invisible without data-size)
widget = soup.find(class_="g-recaptcha")
if widget and widget.get("data-sitekey"):
return widget["data-sitekey"]
return None
sitekey = get_invisible_sitekey("https://staging.example.com/qa-login")
print(f"Sitekey: {sitekey}")
Erro 3: o callback nunca dispara — o token foi aceito, mas nada acontece
Essa é a falha mais comum do reCAPTCHA Invisible, e a mais fácil de não perceber. Na caixa de seleção v2 padrão, colocar o token em g-recaptcha-response já é suficiente. No widget invisível, quase sempre existe uma função JavaScript de callback que precisa ser chamada manualmente — sem ela, o token fica parado no campo oculto e o formulário nunca é submetido.
Como o callback funciona
Primeiro, grecaptcha.execute() dispara o desafio invisível. Em seguida, depois de resolvido, o Google chama a função definida em data-callback. Por fim, é essa função que efetivamente envia o formulário ou dispara a chamada de API.
Correção: localize o callback e chame-o manualmente
Passo 1 — identifique o nome da função
# From HTML: data-callback="onSubmit"
# From JS: callback: onSubmit
# From grecaptcha.render: second argument with callback property
Passo 2 — injete o token e chame o callback (Selenium)
from selenium import webdriver
import requests
import time
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# Get sitekey
sitekey = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-sitekey")
callback_name = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
# Solve with CaptchaAI
task_id = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": driver.current_url,
"invisible": 1
}).text.split("|")[1]
# Poll for result
token = None
for _ in range(60):
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY",
"action": "get",
"id": task_id
}).text
if resp.startswith("OK|"):
token = resp.split("|")[1]
break
# Inject token into the response field
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# CRITICAL: Call the callback function
driver.execute_script(f'{callback_name}("{token}");')
Passo 2 — a mesma lógica com Puppeteer
const puppeteer = require("puppeteer");
const axios = require("axios");
(async () => {
const browser = await puppeteer.launch({ headless: "new" });
const page = await browser.newPage();
await page.goto("https://example.com/form");
// Get sitekey and callback
const { sitekey, callback } = await page.evaluate(() => {
const el = document.querySelector(".g-recaptcha[data-size='invisible']");
return {
sitekey: el?.getAttribute("data-sitekey"),
callback: el?.getAttribute("data-callback"),
};
});
// Submit to CaptchaAI
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: "YOUR_API_KEY",
method: "userrecaptcha",
googlekey: sitekey,
pageurl: page.url(),
invisible: 1,
},
});
const taskId = submitResp.data.split("|")[1];
// Poll for result
let token;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: "YOUR_API_KEY", action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
token = result.data.split("|")[1];
break;
}
}
// Inject token and fire callback
await page.evaluate(
(tok, cb) => {
document.getElementById("g-recaptcha-response").value = tok;
if (cb && typeof window[cb] === "function") {
window[cb](tok);
}
},
token,
callback,
);
await browser.close();
})();
Erro 4: esqueceu o invisible=1 na requisição
Resolver reCAPTCHA Invisible pela CaptchaAI exige o parâmetro invisible=1 na chamada a in.php.
Sem ele, o solver trata a tarefa como uma caixa de seleção v2 comum — o resultado costuma ser ERROR_CAPTCHA_UNSOLVABLE ou um token que o site de destino recusa.
Requisição errada vs. requisição correta
# WRONG — missing invisible=1
params = {
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
}
# CORRECT — includes invisible=1
params = {
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1 # Required for invisible reCAPTCHA
}
response = requests.get("https://ocr.captchaai.com/in.php", params=params)
Erro 5: o token expira antes do envio (janela de 120 s)
Um token de reCAPTCHA Invisible vale por 120 segundos, igual ao v2 padrão. A diferença é que fluxos invisíveis costumam ter mais etapas entre resolver e enviar — preencher outros campos, validar no cliente, esperar uma resposta anterior — e cada etapa consome parte dessa janela.
Como identificar esse sintoma
O formulário devolve um erro genérico depois do envio de teste ao endpoint de QA, o siteverify do lado do servidor responde timeout-or-duplicate, e o token era válido no momento em que chegou, mas demorou demais para ser usado.
Correção: resolva só na hora de enviar
Peça a resolução no último momento possível, imediatamente antes do envio:
import requests
import time
def solve_invisible_recaptcha(api_key, sitekey, page_url):
# Submit task
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
})
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
# Poll for result
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key,
"action": "get",
"id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {result.text}")
raise Exception("Solve timed out after 5 minutes")
# Usage: solve JUST before you need to submit
# 1. Navigate to page and prepare form data first
# 2. THEN solve the captcha
# 3. Inject token and submit immediately
token = solve_invisible_recaptcha("YOUR_API_KEY", sitekey, page_url)
# Submit within 120 seconds of receiving the token
Erro 6: ERROR_BAD_TOKEN_OR_PAGEURL — o Google recusou o token
O site de destino verificou o token junto ao Google e a verificação falhou. As causas mais comuns:
pageurlerrado — não bate com o domínio registrado na sitekey; use exatamente a URL onde o widget carrega.- Token usado em outro domínio — o mesmo token foi reaproveitado entre domínios; resolva de novo com o
pageurldo domínio correto. - Token já usado — o mesmo token foi enviado duas vezes; peça uma solução nova para cada envio.
- IP incompatível — o IP do solver é diferente do IP da sua sessão; adicione o parâmetro
proxypara casar com o IP da sessão. - Sem a flag invisível — resolvido como v2 padrão, mas usado numa página invisível; adicione
invisible=1à requisição de resolução.
O caso do IP incompatível é comum quando os workers de automação rodam numa região diferente da sessão que gerou a sitekey — por exemplo, workers em sa-east-1 (São Paulo) resolvendo para uma sessão que passou por um proxy hospedado em outra região. O Google associa o token ao IP que fez a verificação; se o IP da submissão do formulário não bate com o IP da resolução, o siteverify falha mesmo com um token tecnicamente válido. Alinhar o proxy da chamada in.php com o IP real da sessão resolve a maior parte desses casos.
Checklist de depuração
def debug_invisible_solve(api_key, sitekey, page_url, proxy=None):
"""Run a diagnostic solve with detailed logging."""
print(f"Sitekey: {sitekey}")
print(f"Page URL: {page_url}")
print(f"Proxy: {proxy or 'none'}")
params = {
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
}
if proxy:
params["proxy"] = proxy
params["proxytype"] = "HTTP"
# Submit
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
print(f"Submit response: {resp.text}")
if not resp.text.startswith("OK|"):
return None
task_id = resp.text.split("|")[1]
print(f"Task ID: {task_id}")
# Poll with timing
start = time.time()
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get", "id": task_id
})
elapsed = time.time() - start
print(f" [{elapsed:.0f}s] {result.text[:50]}")
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
print(f"Token received after {elapsed:.0f}s")
print(f"Token length: {len(token)} characters")
print(f"Token starts with: {token[:30]}...")
return token
if result.text != "CAPCHA_NOT_READY":
print(f"FAILED: {result.text}")
return None
print("TIMEOUT after 5 minutes")
return None
Erro 7: a página tem mais de um widget reCAPTCHA
Algumas páginas combinam uma caixa de seleção v2 visível com um reCAPTCHA Invisible — por exemplo, login com v2 e cadastro com invisível na mesma tela.
Resolver o widget errado gera um token válido, só que para o desafio errado: ele não libera a ação que você realmente precisa executar.
Correção: identifique cada widget antes de resolver
from bs4 import BeautifulSoup
def find_all_recaptcha_widgets(html):
soup = BeautifulSoup(html, "html.parser")
widgets = []
for el in soup.find_all(class_="g-recaptcha"):
widgets.append({
"sitekey": el.get("data-sitekey"),
"size": el.get("data-size", "normal"),
"callback": el.get("data-callback"),
"tag": el.name,
"id": el.get("id")
})
return widgets
# Example output:
# [
# {"sitekey": "6LdA...", "size": "normal", "callback": None, "tag": "div", "id": "recaptcha-login"},
# {"sitekey": "6LdB...", "size": "invisible", "callback": "onRegister", "tag": "div", "id": "recaptcha-register"}
# ]
# Use the widget with size="invisible" for the invisible solve
Solver completo com retentativa automática
Esta classe reúne as correções dos sete erros acima num wrapper pronto para produção, com retentativa exponencial e tratamento explícito para cada resposta de erro da CaptchaAI:
import requests
import time
import logging
logger = logging.getLogger(__name__)
class InvisibleRecaptchaSolver:
def __init__(self, api_key, max_retries=3):
self.api_key = api_key
self.max_retries = max_retries
self.base_url = "https://ocr.captchaai.com"
def solve(self, sitekey, page_url, proxy=None):
"""Solve invisible reCAPTCHA with automatic retry on transient errors."""
for attempt in range(1, self.max_retries + 1):
try:
token = self._attempt_solve(sitekey, page_url, proxy)
if token:
return token
except Exception as e:
logger.warning(f"Attempt {attempt} failed: {e}")
if attempt < self.max_retries:
time.sleep(2 ** attempt)
raise Exception(f"Failed to solve after {self.max_retries} attempts")
def _attempt_solve(self, sitekey, page_url, proxy):
params = {
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"invisible": 1
}
if proxy:
params["proxy"] = proxy
params["proxytype"] = "HTTP"
# Submit task
resp = requests.get(f"{self.base_url}/in.php", params=params)
if "ERROR" in resp.text:
error = resp.text.strip()
if error in ("ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"):
raise Exception(f"Configuration error (do not retry): {error}")
if error == "ERROR_ZERO_BALANCE":
raise Exception("Account balance is zero — add funds")
raise Exception(f"Submit error: {error}")
if not resp.text.startswith("OK|"):
raise Exception(f"Unexpected submit response: {resp.text}")
task_id = resp.text.split("|")[1]
# Poll for result
for _ in range(60):
time.sleep(5)
result = requests.get(f"{self.base_url}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text == "CAPCHA_NOT_READY":
continue
if result.text == "ERROR_CAPTCHA_UNSOLVABLE":
logger.warning("Captcha unsolvable — will retry with new task")
return None
raise Exception(f"Poll error: {result.text}")
raise Exception("Solve timed out after 5 minutes")
# Usage
solver = InvisibleRecaptchaSolver("YOUR_API_KEY")
token = solver.solve(
sitekey="6LdKlZEU...",
page_url="https://staging.example.com/qa-login"
)
print(f"Token: {token[:50]}...")
A mesma classe em Node.js:
const axios = require("axios");
class InvisibleRecaptchaSolver {
constructor(apiKey, maxRetries = 3) {
this.apiKey = apiKey;
this.maxRetries = maxRetries;
this.baseUrl = "https://ocr.captchaai.com";
}
async solve(sitekey, pageUrl, proxy) {
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const token = await this._attemptSolve(sitekey, pageUrl, proxy);
if (token) return token;
} catch (err) {
console.warn(`Attempt ${attempt} failed: ${err.message}`);
if (attempt < this.maxRetries) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
}
}
}
throw new Error(`Failed to solve after ${this.maxRetries} attempts`);
}
async _attemptSolve(sitekey, pageUrl, proxy) {
const params = {
key: this.apiKey,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageUrl,
invisible: 1,
};
if (proxy) {
params.proxy = proxy;
params.proxytype = "HTTP";
}
// Submit task
const submitResp = await axios.get(`${this.baseUrl}/in.php`, { params });
if (submitResp.data.includes("ERROR")) {
const error = submitResp.data.trim();
if (["ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"].includes(error)) {
throw new Error(`Configuration error (do not retry): ${error}`);
}
throw new Error(`Submit error: ${error}`);
}
const taskId = submitResp.data.split("|")[1];
// Poll for result
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get(`${this.baseUrl}/res.php`, {
params: { key: this.apiKey, action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
return result.data.split("|")[1];
}
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data === "ERROR_CAPTCHA_UNSOLVABLE") return null;
throw new Error(`Poll error: ${result.data}`);
}
throw new Error("Solve timed out after 5 minutes");
}
}
// Usage
const solver = new InvisibleRecaptchaSolver("YOUR_API_KEY");
solver.solve("6LdKlZEU...", "https://staging.example.com/qa-login").then((token) => {
console.log(`Token: ${token.substring(0, 50)}...`);
});
Checklist antes de pedir ajuda no suporte
Rode esta lista quando o reCAPTCHA Invisible continuar falhando depois das correções acima:
- É realmente invisível, não v2 padrão? Procure
data-size="invisible"ousize: 'invisible'na chamada de render. - A sitekey é a do widget certo? Compare com o
data-sitekeydo widget marcado como invisível, não de outro na mesma página. invisible=1está na requisição? Confira os parâmetros enviados ain.php.pageurlbate exatamente? Copie a URL pelo DevTools do navegador, não uma URL de redirecionamento.- O nome do callback foi identificado? Procure
data-callbackno HTML oucallbackemgrecaptcha.render(). - O token foi injetado E o callback foi chamado? As duas etapas são obrigatórias — o token sozinho não movimenta o formulário.
- O token ainda está dentro da validade? Precisa ser usado em até 120 segundos após a resolução.
- Testou com proxy quando o IP importa? Envie os parâmetros
proxyeproxytype.
Perguntas frequentes
O reCAPTCHA Invisible e o reCAPTCHA v3 são a mesma coisa?
Não. Os dois evitam interação visível, mas são APIs diferentes. O reCAPTCHA Invisible é uma variante do v2 (data-size="invisible") e continua usando uma sitekey de v2 mais o parâmetro invisible=1 na CaptchaAI. Já o reCAPTCHA v3 roda via grecaptcha.execute() com uma action e devolve um score, sem qualquer callback de desafio. Confundir os dois é uma causa frequente de ERROR_WRONG_GOOGLEKEY — confirme que a sitekey realmente veio de um widget v2 marcado como invisível.
CAPCHA_NOT_READY repetido algumas vezes é sinal de problema?
Não. É a resposta normal enquanto o worker ainda processa a tarefa — o reCAPTCHA Invisible normalmente resolve em menos de 30 segundos, e um polling a cada 5 segundos costuma retornar CAPCHA_NOT_READY duas ou três vezes antes do token. Só trate como falha se o loop ultrapassar os 5 minutos de timeout; nesse caso, descarte a tarefa e envie uma nova em vez de continuar consultando a mesma id.
Por que o token funciona em teste, mas falha em produção?
O motivo mais comum é incompatibilidade de IP. Em teste, o IP do solver e o IP de quem está testando muitas vezes ficam próximos; em produção, o IP do solver e o IP do seu servidor são diferentes. Adicione um parâmetro proxy que corresponda ao IP real da sessão de produção para eliminar essa causa (veja o Erro 6).
Quanto tempo o CaptchaAI leva para resolver o reCAPTCHA Invisible?
Normalmente menos de 30 segundos. Desafios invisíveis costumam ter menor latência que a caixa de seleção v2 porque dependem de análise de risco, não de reconhecimento de imagem — mas o tempo total do seu fluxo também inclui o polling e a execução do callback, então meça de ponta a ponta no seu próprio ambiente.
Próximas etapas
- Como funciona o reCAPTCHA Invisible e como resolvê-lo — o mecanismo por trás do widget invisível
- Como resolver reCAPTCHA v2 usando API — a solução padrão v2, para comparar
- reCAPTCHA v2 comum resolvendo erros e correções — os erros equivalentes na versão v2
- Códigos de erro CaptchaAI: referência completa — tabela completa de códigos de erro
Artigos relacionados
- Resolva Recaptcha Python invisível
- Erros e correções comuns de captcha de imagem de grade
- Recaptcha V2 vs Invisível