API Tutorials

Como resolver o callback do reCAPTCHA v2 com a API

Se o token do reCAPTCHA v2 chega certinho mas o formulário não reage, o problema quase sempre é o mesmo: o site usa um callback em JavaScript em vez do campo oculto g-recaptcha-response. A correção não está na chamada de API — está em descobrir o nome dessa função e chamá-la você mesmo, com o token em mãos.

Esse padrão é comum em formulários de login e cadastro que validam o reCAPTCHA no próprio JavaScript da página, sem depender do envio tradicional do formulário. Preencher o campo oculto nesses casos não faz nada: a página ignora o valor e fica esperando o disparo da função.

Este guia mostra como identificar um reCAPTCHA v2 com callback, resolvê-lo pela API da CaptchaAI e invocar a função certa em Python, Node.js e PHP. A chamada à API é idêntica ao fluxo padrão — só muda a etapa de entrega do token.

Primeira vez com a API da CaptchaAI? Comece pelo fluxo padrão do reCAPTCHA v2 e volte aqui quando encontrar um site que usa callback.


Pré-requisitos para resolver o callback

Antes de sair copiando código, separe estes cinco itens — sem eles nenhum dos exemplos abaixo funciona:

  • Chave de API da CaptchaAI — obtenha a sua em captchaai.com/api.php. É uma sequência de 32 caracteres.
  • URL da página de destino — o endereço completo onde o widget reCAPTCHA v2 é carregado.
  • Sitekey do reCAPTCHA v2 — a chave pública (a sitekey) vinculada à instância do widget.
  • Ferramenta de automação do navegador — Selenium, Puppeteer ou Playwright. Você precisa executar JavaScript na página para invocar o callback.
  • Nome da função de callback — a função JavaScript que o site espera receber o token resolvido.

Como saber se o reCAPTCHA v2 do site usa callback

No fluxo padrão, o token resolvido vai para uma textarea oculta chamada g-recaptcha-response. Numa implementação com callback, essa área nem é usada — o site chama uma função JavaScript direto. Normalmente é decisão de arquitetura, não capricho: frameworks de single-page application preferem controlar toda a validação em JavaScript, sem depender do envio nativo do formulário, ou o time quer disparar analytics e verificações extras no momento exato em que o token chega. Nenhum dos dois motivos muda como você resolve o CAPTCHA pela API — só como você entrega o token no fim. Três sinais no código-fonte confirmam qual dos dois você está vendo.

Três sinais no código-fonte

O primeiro é o atributo data-callback na div do widget. Inspecione o código-fonte da página:

<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
     data-callback="SubmitToken">
</div>

Se data-callback existir, o site usa callback. O valor (SubmitToken, no exemplo acima) é o nome da função que você vai chamar depois.

O segundo é a chamada grecaptcha.render() no JavaScript da página:

grecaptcha.render('recaptcha-container', {
  sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
  callback: userVerified
});

A propriedade callback nomeia a função — neste exemplo, userVerified.

O terceiro é abrir o console do navegador na página de destino e inspecionar a configuração interna do reCAPTCHA:

___grecaptcha_cfg.clients[0]

Navegue na árvore de objetos até achar a propriedade callback. O caminho exato varia por site — pode ser clients[0].aa.l.callback ou outro, dependendo da versão do reCAPTCHA e do nível de minificação do JavaScript. Se a página tiver mais de um widget, verifique clients[1], clients[2] e assim por diante.

Script para detectar o callback automaticamente

Cole isto no console do navegador para encontrar o nome da função sem precisar navegar manualmente pela árvore de objetos:

// Check data-callback attributes
document.querySelectorAll('[data-callback]').forEach(el => {
  console.log('data-callback:', el.getAttribute('data-callback'));
});

// Check internal config
if (typeof ___grecaptcha_cfg !== 'undefined') {
  Object.keys(___grecaptcha_cfg.clients).forEach(key => {
    const client = ___grecaptcha_cfg.clients[key];
    console.log(`Client ${key}:`, JSON.stringify(client, null, 2));
  });
}

Callback x fluxo padrão do reCAPTCHA v2: o que muda

A chamada para a API da CaptchaAI é idêntica nos dois casos. Passo a passo, é só a etapa 4 que diverge:

  • 1. Enviar para a CaptchaAImethod=userrecaptcha + sitekey + URL da página. Igual nos dois fluxos.
  • 2. Consultar o resultadoaction=get + ID do captcha. Igual nos dois fluxos.
  • 3. Receber o token — mesmo formato de token nos dois casos.

As duas etapas finais são onde os fluxos realmente se separam:

  • 4. Entregar o token — no padrão, você define o valor do campo g-recaptcha-response; no callback, você chama a função de callback passando o token.
  • 5. Enviar o formulário — no padrão, você dispara o submit; com callback, isso geralmente já é automático.

Atenção: não defina g-recaptcha-response em implementações com callback. A página ignora esse campo e só reage quando a função de callback dispara. Se você preencher o campo sem chamar o callback, o CaptchaAI mostra o token resolvido, mas para o site o CAPTCHA nunca foi respondido.


Por que usar a CaptchaAI para o callback do reCAPTCHA v2

  • Mesma chamada de API — o fluxo de envio e consulta é idêntico ao padrão reCAPTCHA v2, sem parâmetro extra nenhum.
  • Taxa de sucesso — alta nos dois casos, porque callback e fluxo padrão usam o mesmo solucionador de reCAPTCHA v2.
  • Velocidade de resolução — menos de 60 segundos.
  • Compatibilidade do token — o token retornado funciona tanto com a injeção em g-recaptcha-response quanto com a chamada de callback.
  • Preços — planos por thread a partir do BASIC (US$ 15/mês, 5 threads), com resoluções ilimitadas por thread.

O token que a CaptchaAI devolve é o mesmo, não importa como o site implementa o reCAPTCHA v2. A diferença fica inteiramente no seu código do lado do cliente — em como você entrega esse token à página.

Se os formulários que você testa rodam em infraestrutura no Brasil, vale rodar o worker de automação numa região como sa-east-1 (São Paulo) da AWS: reduz o RTT entre carregar a página, consultar res.php e disparar o callback — o que faz diferença em páginas com vários widgets reCAPTCHA.


Fluxo de resolução passo a passo

Do envio à página processando o resultado, é este o caminho que o token percorre:

Page → extract sitekey + pageurl + callback name
                    ↓
      POST to in.php (method=userrecaptcha)
                    ↓
           receive captcha ID
                    ↓
         wait 15–20 seconds
                    ↓
      GET res.php (action=get, id=…)
          ↓                    ↓
   CAPCHA_NOT_READY       status=1 → token
    (wait 5s, retry)            ↓
                     invoke callback(token)
                              ↓
               site processes token automatically

Implementação em Python com Selenium

O código abaixo detecta o nome do callback direto no DOM, resolve o token pela API da CaptchaAI e invoca a função com execute_script:

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

API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
PAGE_URL = "https://staging.example.com/qa-login"
CALLBACK_NAME = "SubmitToken"  # The callback function name from the page

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_recaptcha_v2(api_key, sitekey, pageurl):
    """Submit a reCAPTCHA v2 task and return the solved token."""

    # Step 1: Submit the captcha
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": 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}")

    # Step 2: Wait before first poll
    time.sleep(15)

    # Step 3: 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("reCAPTCHA v2 solve timed out")


def detect_callback_name(driver):
    """Detect the reCAPTCHA callback function name from the page."""

    # Try data-callback attribute first
    callback = driver.execute_script("""
        const el = document.querySelector('[data-callback]');
        if (el) return el.getAttribute('data-callback');
        return null;
    """)
    if callback:
        return callback

    # Try internal reCAPTCHA config
    callback = driver.execute_script("""
        if (typeof ___grecaptcha_cfg === 'undefined') return null;
        const clients = ___grecaptcha_cfg.clients;
        for (const key of Object.keys(clients)) {
            const client = clients[key];
            // Walk the object tree to find a callback function
            const json = JSON.stringify(client);
            const match = json.match(/"callback":"(\\w+)"/);
            if (match) return match[1];
        }
        return null;
    """)
    return callback


# Main workflow
driver = webdriver.Chrome()
driver.get(PAGE_URL)

# Detect the callback name (or use the known name)
detected = detect_callback_name(driver)
callback_name = detected or CALLBACK_NAME
print(f"Using callback: {callback_name}")

# Solve the CAPTCHA
token = solve_recaptcha_v2(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")

# Invoke the callback with the token
driver.execute_script(f"{callback_name}(arguments[0]);", token)
print("Callback invoked — site should process the token automatically")

# Wait for the page to process
time.sleep(3)
driver.quit()

O que o código faz:

  • Envia a sitekey e o pageurl para in.php com method=userrecaptcha — igual ao fluxo padrão.
  • Consulta res.php a cada 5 segundos até o token ficar pronto.
  • Detecta o nome da função de callback direto no DOM da página.
  • Chama o callback com o token resolvido usando execute_script.
  • Dali em diante, o JavaScript do próprio site cuida do resto — envio de formulário, validação ou redirecionamento.

Implementação em Node.js com Puppeteer

A lógica é a mesma do Python — só troca a biblioteca de automação:

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://staging.example.com/qa-login";
const CALLBACK_NAME = "SubmitToken";

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 solveRecaptchaV2(apiKey, sitekey, pageurl) {
  // Step 1: Submit the captcha
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "userrecaptcha",
      googlekey: 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}`);

  // Step 2: Wait before first poll
  await sleep(15_000);

  // Step 3: 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("reCAPTCHA v2 solve timed out");
}

async function detectCallbackName(page) {
  return page.evaluate(() => {
    // Try data-callback attribute
    const el = document.querySelector("[data-callback]");
    if (el) return el.getAttribute("data-callback");

    // Try internal config
    if (typeof ___grecaptcha_cfg !== "undefined") {
      const clients = ___grecaptcha_cfg.clients;
      for (const key of Object.keys(clients)) {
        const json = JSON.stringify(clients[key]);
        const match = json.match(/"callback":"(\w+)"/);
        if (match) return match[1];
      }
    }

    return null;
  });
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto(PAGE_URL, { waitUntil: "networkidle2" });

  // Detect callback
  const detected = await detectCallbackName(page);
  const callbackName = detected || CALLBACK_NAME;
  console.log(`Using callback: ${callbackName}`);

  // Solve the CAPTCHA
  const token = await solveRecaptchaV2(API_KEY, SITEKEY, PAGE_URL);
  console.log(`Solved token: ${token.slice(0, 80)}...`);

  // Invoke the callback
  await page.evaluate(
    (name, tkn) => {
      window[name](tkn);
    },
    callbackName,
    token
  );
  console.log("Callback invoked — site should process the token automatically");

  await sleep(3_000);
  await browser.close();
})();

Implementação em PHP: resolução no servidor

A chamada de API é idêntica em PHP. Só que invocar o callback exige um navegador de verdade, então este exemplo cobre apenas a resolução — a parte de servidor. Para a etapa de injeção, use uma ferramenta de navegador headless (por exemplo, PHP WebDriver).

<?php
$apiKey  = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://staging.example.com/qa-login";

// Step 1: Submit
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
    "key"       => $apiKey,
    "method"    => "userrecaptcha",
    "googlekey" => $sitekey,
    "pageurl"   => $pageurl,
    "json"      => 1,
]));

$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
    die("Submit failed: " . $submit);
}

$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";

// Step 2: Wait and poll
sleep(15);

for ($i = 0; $i < 60; $i++) {
    $result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $captchaId,
        "json"   => 1,
    ]));

    $resultData = json_decode($result, true);

    if ($resultData["request"] === "CAPCHA_NOT_READY") {
        sleep(5);
        continue;
    }

    if ($resultData["status"] === 1) {
        $token = $resultData["request"];
        echo "Solved token: " . substr($token, 0, 80) . "...\n";
        // Pass $token to your browser automation to invoke the callback
        break;
    }

    die("Polling error: " . $result);
}

Com o token em mãos, use uma ferramenta de automação do navegador (por exemplo, php-webdriver) para executar:

SubmitToken("TOKEN_FROM_CAPTCHAAI");

Erros comuns e solução de problemas

A maioria dos problemas com callback cai em uma destas causas — confira a tabela antes de sair depurando linha por linha:

Sintoma Causa provável O que fazer
Token resolvido, mas a página não reage Você definiu g-recaptcha-response em vez de chamar o callback Confira se o widget tem data-callback ou callback em grecaptcha.render() e invoque essa função com o token
ReferenceError: SubmitToken is not defined, ou função não definida Nome de callback errado, não carregado ainda, ou minificado/ofuscado no código-fonte Confira de novo data-callback, grecaptcha.render() ou a configuração interna; aguarde a página carregar por completo; em sites minificados, use o console em runtime para achar a referência real da função
Callback aciona a instância errada de reCAPTCHA Em páginas com vários widgets, o callback usado está em outro índice de cliente Verifique ___grecaptcha_cfg.clients[1], clients[2] etc. e combine cada widget com o formulário correspondente
O callback nunca dispara, mesmo com o nome certo Você chamou o callback antes da página estar pronta — a função ainda não existe no contexto da página Aguarde DOMContentLoaded ou networkidle antes de invocar
Comportamento lembra reCAPTCHA Invisible Algumas implementações invisíveis também usam callback Veja se data-size="invisible" está presente — se estiver, consulte Como resolver reCAPTCHA Invisible com a API
O token funciona no fluxo padrão, mas falha nesta página Você está diante de uma implementação com callback Siga os passos de detecção descritos acima e mude para a invocação do callback
ERROR_BAD_TOKEN_OR_PAGEURL O par sitekey/pageurl é inválido Extraia os dois valores de novo, direto da página — sem relação com callback ou fluxo padrão
O callback dispara mais de uma vez O listener é registrado a cada re-render (comum em SPAs) ou o script chama execute_script/page.evaluate mais de uma vez no mesmo token Chame o callback uma única vez por token resolvido e, se o front-end for reativo, registre o listener fora do ciclo de render
ERROR_CAPTCHA_UNSOLVABLE O desafio não pôde ser resolvido Tente de novo com uma requisição nova — não é específico de callback

Referência completa: consulte Erros comuns na resolução do reCAPTCHA v2 para os demais códigos de erro da API.


Projeto completo no GitHub

Quer um projeto pronto, com setup de ambiente, polling, retentativas e tratamento de erro já resolvidos? Veja o exemplo executável completo no GitHub →


Perguntas frequentes

Meu formulário não avança mesmo com o token resolvido — o que verificar primeiro?

Confira se o widget tem data-callback ou a propriedade callback dentro de grecaptcha.render(). Se tiver, preencher g-recaptcha-response não faz nada — você precisa chamar essa função com o token. É a causa mais comum desse sintoma.

Como encontro o nome da função de callback?

Três lugares confirmam: o atributo data-callback na div do reCAPTCHA, a propriedade callback dentro de grecaptcha.render() no JavaScript da página, ou o objeto ___grecaptcha_cfg.clients[0] no console — navegue na árvore até achar callback.

Selenium, Puppeteer e Playwright resolvem o callback do mesmo jeito?

A chamada para a API da CaptchaAI é idêntica nos três. Só muda a sintaxe para rodar JavaScript na página — execute_script no Selenium, page.evaluate() no Puppeteer e no Playwright. A lógica de detectar e disparar o callback é a mesma.

Preciso de um navegador para chamar o callback?

Sim. O callback é uma função JavaScript que só existe no contexto da página carregada. Você precisa de Selenium, Puppeteer, Playwright ou outra ferramenta que execute JavaScript ali dentro — uma chamada de API sozinha não alcança essa função.

Esse fluxo serve para automação em produção, ou só para testes manuais?

Serve para automação recorrente, desde que em ambiente autorizado — QA, staging ou monitoramento próprio. Se o pipeline grava o token ou o payload de depuração em log, trate isso como dado sensível sob a LGPD e mantenha a retenção curta.


Comece a resolver o callback do reCAPTCHA v2

  1. Obtenha sua chave de APIcaptchaai.com/api.php
  2. Detecte o nome do callback — confira data-callback, grecaptcha.render() ou a configuração interna
  3. Copie o código Python, Node.js ou PHP acima — troque os placeholders pela sua chave, sitekey, pageurl e nome do callback
  4. Rode — o token chega em menos de 60 segundos, o callback dispara e a página processa o resultado
  5. Travou? Comece por Erros comuns na resolução do reCAPTCHA v2 ou leia a documentação completa da API CaptchaAI

Guias relacionados

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