Explainers

reCAPTCHA v2 invisível: detecção e solução de gatilhos

Seu script envia o formulário de staging, recebe HTTP 200 e o cadastro não aparece do outro lado. Nenhum erro, nenhum log, nenhuma caixa de seleção na tela. O culpado costuma ser um reCAPTCHA v2 invisível: dispara sozinho no clique do botão e só exibe desafio quando a análise de risco do Google desconfia da sessão.

Sem widget para procurar, o diagnóstico começa pelo DOM. Veja as quatro assinaturas que revelam a variante, como achar o elemento que a dispara e como resolvê-la com a API da CaptchaAI usando invisible=1.


Quatro formas de detectar o reCAPTCHA invisível

Rode as verificações no console, nesta ordem. A primeira que retornar algo fecha o diagnóstico; as demais só valem sem a div.

  1. Atributo data-size da div .g-recaptcha
  2. Presença do selo grecaptcha-badge
  3. Chamadas a grecaptcha.execute no JavaScript inline
  4. Parâmetro render na tag de script do reCAPTCHA

1. Leia o atributo data-size

Quando a div existe, o próprio tamanho declara a variante.

// Browser console
const widgets = document.querySelectorAll('.g-recaptcha');
widgets.forEach((el, i) => {
  const size = el.getAttribute('data-size');
  const sitekey = el.getAttribute('data-sitekey');
  console.log(`Widget ${i}: size=${size}, sitekey=${sitekey}`);
  if (size === 'invisible') {
    console.log('  → This is Invisible reCAPTCHA');
  }
});

2. Procure o selo no canto da tela

O selo aparece quando não há widget visível — a alternativa permitida é um aviso em texto no formulário.

const badge = document.querySelector('.grecaptcha-badge');
if (badge) {
  console.log('reCAPTCHA badge found — likely Invisible reCAPTCHA');
  console.log('Badge visibility:', getComputedStyle(badge).visibility);
}

3. Cace chamadas a grecaptcha.execute

Sem div alguma, a integração é programática e a pista está no JavaScript inline.

// Look for grecaptcha.execute in page scripts
document.querySelectorAll('script:not([src])').forEach(s => {
  if (s.textContent.includes('grecaptcha.execute')) {
    console.log('Found grecaptcha.execute — Invisible reCAPTCHA');
    const match = s.textContent.match(/grecaptcha\.execute\s*\(\s*['"]?([^'",\s)]+)/);
    if (match) console.log('Sitekey:', match[1]);
  }
});

4. Inspecione o parâmetro render da tag de script

Se a URL do script traz render= com uma sitekey em vez de render=explicit, a ativação é automática.

document.querySelectorAll('script[src*="recaptcha"]').forEach(s => {
  if (s.src.includes('render=') && !s.src.includes('render=explicit')) {
    console.log('Invisible/v3 reCAPTCHA detected in script:', s.src);
  }
});
  • Guarde a sitekey: é o único parâmetro que muda entre páginas.
  • Transforme as checagens em teste de QA: você descobre no deploy quando o frontend troca a configuração.

O que muda em relação ao v2 padrão

A diferença não está no algoritmo, e sim em quem inicia o desafio: no v2 padrão o usuário clica; no invisível, o site chama grecaptcha.execute() no envio.

Característica reCAPTCHA v2 padrão reCAPTCHA v2 invisível
Widget visível Sim (caixa de seleção) Não (apenas o selo no canto)
Interação do usuário Clique na caixa Automático no envio do formulário
data-size normal ou compact invisible
Pop-up de desafio Sempre possível Só para sessões suspeitas
Elemento no DOM Div .g-recaptcha Div .g-recaptcha ou chamada programática
  • Seu bot não acha ponto de entrada: não há elemento com que interagir.
  • Em staging com pouco tráfego a análise libera a sessão sem exibir imagem, e o time conclui que "não tem CAPTCHA aqui" — até rodar em volume.

Resolvendo com a API da CaptchaAI

A única diferença é o parâmetro invisible=1 no envio; sem esse sinal, o token tende a ser recusado pelo servidor de verificação.

  • googlekey: a sitekey (chave pública do widget) extraída no passo anterior
  • pageurl: a URL exata da página do formulário
  • invisible: 1, obrigatório nesta variante
import requests
import time

API_KEY = "YOUR_API_KEY"

# Submit with invisible flag
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6Le-SITEKEY",
    "pageurl": "https://staging.example.com/qa-login",
    "invisible": "1",  # critical for Invisible reCAPTCHA
    "json": "1",
}).json()

if resp["status"] != 1:
    raise Exception(f"Submit error: {resp['request']}")

task_id = resp["request"]
print(f"Submitted: {task_id}")

# Poll for result
for _ in range(24):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()

    if result["status"] == 1:
        print(f"Token: {result['request'][:50]}...")
        break
    if result["request"] != "CAPCHA_NOT_READY":
        raise Exception(f"Error: {result['request']}")

Em Node.js o envio é idêntico — muda o cliente HTTP.

const axios = require('axios');

const resp = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: 'YOUR_API_KEY',
    method: 'userrecaptcha',
    googlekey: '6Le-SITEKEY',
    pageurl: 'https://staging.example.com/qa-login',
    invisible: 1,
    json: 1,
  }
});
const taskId = resp.data.request;
console.log(`Submitted: ${taskId}`);
  • O consumo segue o modelo de threads: paga-se por concorrência, não por resolução.
  • O BASIC (US$ 15/mês, 5 threads) atende uma suíte com poucos formulários por deploy; para monitoramento autorizado contínuo, o ADVANCE (US$ 90/mês, 50 threads) encaixa melhor.
  • Escopo: reCAPTCHA v2 e v3, Cloudflare Turnstile e Challenge, GeeTest v3 e CAPTCHAs de imagem e grade. hCaptcha e FunCaptcha não são suportados.

Envio controlado ao endpoint de QA

Com o token em mãos, preencha g-recaptcha-response e acione a função declarada em data-callback. Só então envie o formulário.

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")

# After solving, inject and trigger
driver.execute_script("""
    // Set the token
    document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];

    // Find and trigger the callback
    var widget = document.querySelector('.g-recaptcha');
    var callbackName = widget ? widget.getAttribute('data-callback') : null;

    if (callbackName && typeof window[callbackName] === 'function') {
        window[callbackName](arguments[0]);
    }
""", token)

# Submit the form
driver.find_element(By.CSS_SELECTOR, "form#login").submit()
  • Pular o callback reproduz o sintoma da abertura: o formulário parte, o servidor responde sem erro e nada acontece.
  • O token tem validade curta: resolva e injete na mesma execução, sem reaproveitar entre rodadas.

Localize o elemento que dispara o desafio

Falta saber quem chama o desafio. O caso simples é o botão que carrega a classe do widget.

<button class="g-recaptcha"
        data-sitekey="6Le-SITEKEY"
        data-callback="onSubmit"
        data-size="invisible">
  Submit
</button>

O caso chato é a ativação por código, escondida em um listener de clique.

// Site's code
document.getElementById('submit-btn').addEventListener('click', function() {
  grecaptcha.execute();
});
  • Verifique os dois padrões antes de descartar a variante invisível.
  • Guarde o data-callback: é a função que você chama depois de receber o token.
// Find elements with g-recaptcha class that are buttons
document.querySelectorAll('button.g-recaptcha, input.g-recaptcha').forEach(el => {
  console.log('Trigger element:', el.tagName, el.textContent.trim());
  console.log('  data-sitekey:', el.getAttribute('data-sitekey'));
  console.log('  data-callback:', el.getAttribute('data-callback'));
});

Se a varredura não retornar nada, volte ao método 3: a chamada está no script inline.


Solução de problemas

Estes quatro sintomas cobrem quase todo chamado sobre a variante.

Sintoma Causa provável Como corrigir
Token recusado pelo site Falta invisible=1 no envio Inclua invisible: "1" na requisição à CaptchaAI
Não encontro a sitekey A página não tem div .g-recaptcha Procure chamadas a grecaptcha.render() ou grecaptcha.execute() no JavaScript inline
O formulário envia, mas nada acontece O callback não foi acionado Leia data-callback no botão e chame essa função com o token
Nenhum CAPTCHA aparece nos testes Só é exibido para sessões suspeitas Verifique o elemento grecaptcha-badge ou as tags de script do reCAPTCHA

Cenário prático: homologação que falha sem erro

Uma equipe em São Paulo roda testes de regressão em staging.example.com a cada deploy. O cadastro passou a falhar em 3 de 10 execuções, sempre sem mensagem de erro.

O console não mostrou div .g-recaptcha, mas o método 3 achou uma chamada a grecaptcha.execute() no script inline: o frontend migrara para o invisível na sprint anterior. Com a sitekey do próprio trecho, bastou incluir invisible=1 e chamar o callback do botão.

  • Automatize apenas ambientes que você controla ou tem autorização para testar.
  • Use dados fictícios: a LGPD e o RGPD tratam dados pessoais de teste com o mesmo rigor dos de produção.

Perguntas frequentes

Como sei se é v2 invisível ou v3?

Olhe a chamada de execução. O v2 invisível usa grecaptcha.execute() sem action; o v3 usa grecaptcha.execute(sitekey, {action: 'submit'}) e traz render=SITEKEY na URL do script.

Preciso de um navegador para resolver?

Para obter o token, não: a API exige apenas a sitekey e a URL da página. O navegador entra só na etapa final, para injetar o token e disparar o callback.

Quanto tempo leva a resolução?

Depende da fila e do tipo de desafio. Siga o exemplo em Python: aguarde alguns segundos antes da primeira consulta e refaça o polling em intervalos regulares, tratando CAPCHA_NOT_READY como resposta normal, não como erro.

O selo do reCAPTCHA some se eu resolver pela API?

Não. O selo é renderizado pelo site e continua onde estava; muda apenas o valor de g-recaptcha-response que você envia.

Posso resolver vários formulários em paralelo?

Sim, e as threads do plano definem o teto. Cada desafio em andamento ocupa uma thread; ao terminar, ela fica livre. Não há cobrança por resolução.


Comece a resolver o reCAPTCHA invisível hoje

Pegue sua chave de API em captchaai.com e rode o exemplo em Python no seu formulário de staging.


Guias relacionados

Os comentários estão desativados para este artigo.