API Tutorials

Como resolver o reCAPTCHA v2 Enterprise com Node.js

Para quem integra a API, a diferença entre o reCAPTCHA v2 padrão e o v2 Enterprise cabe em um parâmetro: enterprise=1. O widget é o mesmo checkbox "Não sou um robô", o campo de resposta continua sendo g-recaptcha-response e o método enviado à CaptchaAI continua sendo userrecaptcha. O que muda é o back-end que valida o token — o do Google Enterprise, mais rigoroso com o contexto da sessão que gerou o desafio.

Isso afeta três pontos do seu código Node.js: como confirmar que a página é Enterprise, o que acrescentar na requisição e o que fazer com o user_agent devolvido. Os quatro passos abaixo cobrem esse caminho; o script completo está no fim.


Antes de começar: o que ter em mãos

Requisito Detalhes
Chave de API da CaptchaAI Painel da CaptchaAI
Node.js 14+ Com fetch nativo ou node-fetch
sitekey Parâmetro k= da URL de anchor do Enterprise
URL da página Endereço completo onde o CAPTCHA aparece
action (opcional) Parâmetro sa= da mesma URL de anchor

Passo 1: confirme que a página usa mesmo Enterprise v2

Abra o DevTools, vá até a aba Network e procure a requisição de anchor carregada pelo widget — três sinais confirmam o diagnóstico:

https://www.google.com/recaptcha/enterprise/anchor?ar=1&k=6LdxxXXxAAAAAAcX...&sa=LOGIN&...
  • O script vem de /recaptcha/enterprise.js ou de /enterprise/anchor
  • O parâmetro k= é a sitekey (chave pública do widget)
  • O parâmetro sa=, quando existe, é a action

Se a URL for /recaptcha/api2/anchor, o site usa o v2 padrão e você não deve enviar enterprise=1 — marcar um widget comum como Enterprise é causa frequente de token recusado.


Passo 2: envie a tarefa para a CaptchaAI

A requisição vai para in.php e carrega quatro informações:

  • method=userrecaptcha — o mesmo método do v2 padrão
  • googlekey — a sitekey lida no k=
  • pageurl — a URL completa do formulário
  • enterprise=1 — o marcador que troca o back-end de validação

Acrescente json=1 para receber a resposta em JSON. O action entra apenas quando o anchor trouxe sa=:

const API_KEY = "YOUR_API_KEY";

async function submitTask(sitekey, pageurl, action) {
  const params = new URLSearchParams({
    key: API_KEY,
    method: "userrecaptcha",
    googlekey: sitekey,
    pageurl: pageurl,
    enterprise: "1",
    json: "1",
  });

  if (action) {
    params.set("action", action);
  }

  const response = await fetch(
    `https://ocr.captchaai.com/in.php?${params}`
  );
  const data = await response.json();

  if (data.status !== 1) {
    throw new Error(`Submit failed: ${data.request}`);
  }

  console.log(`Task submitted. ID: ${data.request}`);
  return data.request;
}

A resposta traz o ID da tarefa em data.request. Guarde esse valor: ele é a chave de tudo que vem depois.


Passo 3: consulte o resultado com polling

O tempo de resolução do reCAPTCHA v2 Enterprise fica abaixo de 60 s, e o ritmo do polling sai daí:

  • espere 20 s antes da primeira consulta
  • repita a cada 5 s, no máximo 30 vezes
  • trate CAPCHA_NOT_READY como "ainda processando", não como erro

O loop abaixo faz exatamente isso:

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function pollResult(taskId) {
  await delay(20000);

  for (let attempt = 0; attempt < 30; attempt++) {
    const params = new URLSearchParams({
      key: API_KEY,
      action: "get",
      id: taskId,
      json: "1",
    });

    const response = await fetch(
      `https://ocr.captchaai.com/res.php?${params}`
    );
    const data = await response.json();

    if (data.status === 1) {
      console.log(`Solved. Token: ${data.request.substring(0, 60)}...`);
      return {
        token: data.request,
        userAgent: data.user_agent || "",
      };
    }

    if (data.request !== "CAPCHA_NOT_READY") {
      throw new Error(`Solve failed: ${data.request}`);
    }

    console.log(`Attempt ${attempt + 1}: not ready, waiting 5s...`);
    await delay(5000);
  }

  throw new Error("Solve timed out");
}

Repare que a função devolve o token e o user_agent. Guardar esse segundo valor separa uma integração estável de uma que falha de forma intermitente.


Passo 4: envie o token no campo g-recaptcha-response

O token vai para o formulário no campo g-recaptcha-response. Quando a API devolve um user_agent, use exatamente esse valor no cabeçalho — o back-end Enterprise compara o contexto do token com quem o apresenta:

async function submitForm(token, userAgent) {
  const headers = { "Content-Type": "application/x-www-form-urlencoded" };

  if (userAgent) {
    headers["User-Agent"] = userAgent;
  }

  const response = await fetch("https://example.com/api/login", {
    method: "POST",
    headers,
    body: new URLSearchParams({
      username: "user",
      password: "pass",
      "g-recaptcha-response": token,
    }),
  });

  console.log(`Response status: ${response.status}`);
  return response;
}

Antes de dar como pronto, confira três pontos:

  • o token viaja no corpo do POST, não na query string
  • a pageurl enviada é a mesma página do formulário
  • o User-Agent da requisição é o que veio de res.php

Erros mais comuns e como corrigir

Erro Causa Correção
ERROR_WRONG_USER_KEY Formato da chave de API inválido A chave tem 32 caracteres; copie-a de novo no painel
ERROR_KEY_DOES_NOT_EXIST Chave não reconhecida Confirme a chave ativa em captchaai.com
ERROR_ZERO_BALANCE Saldo insuficiente Recarregue a conta antes de reenviar a fila
ERROR_BAD_TOKEN_OR_PAGEURL sitekey ou pageurl incorretos Extraia o k= do anchor Enterprise e use a URL completa da página
ERROR_CAPTCHA_UNSOLVABLE A tarefa não pôde ser resolvida Verifique se a sitekey é mesmo Enterprise v2 e reenvie com backoff exponencial
Token recusado pelo site User-Agent diferente do usado na resolução Aplique o user_agent devolvido em res.php nos cabeçalhos

Script completo em Node.js

Os quatro passos em um arquivo só, com as constantes no topo para trocar:

const API_KEY = "YOUR_API_KEY";
const SITE_KEY = "6LdxxXXxAAAAAAcXxxXxxX91xxxxxxxx8xxOx7A";
const PAGE_URL = "https://staging.example.com/qa-login";
const ACTION = "LOGIN"; // optional — omit if not in anchor URL

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveRecaptchaV2Enterprise() {
  // Submit task
  const submitParams = new URLSearchParams({
    key: API_KEY,
    method: "userrecaptcha",
    googlekey: SITE_KEY,
    pageurl: PAGE_URL,
    enterprise: "1",
    action: ACTION,
    json: "1",
  });

  const submitRes = await fetch(
    `https://ocr.captchaai.com/in.php?${submitParams}`
  );
  const submitData = await submitRes.json();

  if (submitData.status !== 1) {
    throw new Error(`Submit error: ${submitData.request}`);
  }

  const taskId = submitData.request;
  console.log(`Task ID: ${taskId}`);

  // Poll for result
  await delay(20000);

  for (let i = 0; i < 30; i++) {
    const pollParams = new URLSearchParams({
      key: API_KEY,
      action: "get",
      id: taskId,
      json: "1",
    });

    const pollRes = await fetch(
      `https://ocr.captchaai.com/res.php?${pollParams}`
    );
    const pollData = await pollRes.json();

    if (pollData.status === 1) {
      return {
        token: pollData.request,
        userAgent: pollData.user_agent || "",
      };
    }

    if (pollData.request !== "CAPCHA_NOT_READY") {
      throw new Error(`Solve error: ${pollData.request}`);
    }

    await delay(5000);
  }

  throw new Error("Solve timed out");
}

(async () => {
  const { token, userAgent } = await solveRecaptchaV2Enterprise();
  console.log(`Token: ${token.substring(0, 60)}...`);
  if (userAgent) console.log(`User-Agent: ${userAgent}`);
})();

Saída esperada:

Task ID: 73849562810
Token: 03AGdBq24PBCqLmOx2V4pGHJjkR2xZ1r...
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...

Threads, tempo de resolução e escolha do plano

A CaptchaAI cobra por thread simultânea, não por resolução: cada plano inclui resoluções ilimitadas dentro das threads contratadas. Uma thread é um CAPTCHA em andamento — quando ele termina, ela aceita a próxima tarefa.

Como o Enterprise v2 resolve em menos de 60 s, dá para dimensionar pelo pior caso. Uma suíte de regressão que valida 30 formulários com CAPTCHA por execução termina assim:

Plano Threads Lote de 30 formulários
BASIC (US$ 15/mês) 5 ~6 minutos
STANDARD (US$ 30/mês) 15 ~2 minutos
ADVANCE (US$ 90/mês) 50 vários branches em paralelo

Um detalhe que costuma passar batido: com workers em sa-east-1 (São Paulo), o RTT até a API entra no orçamento de cada iteração do polling. Isso não muda o tempo de resolução, mas justifica o intervalo de 5 s — consultar a cada 1 s não traz o token mais cedo.

O escopo do fluxo é ambiente próprio ou QA autorizado, como o https://staging.example.com/qa-login do script: formulários seus e dados fictícios. Se a automação tocar dados pessoais reais, considere as obrigações da LGPD antes de registrar payloads em log.


Perguntas frequentes

O Enterprise v2 exige uma chave de API separada?

Não. É a mesma chave e o mesmo method=userrecaptcha do v2 padrão; muda só o enterprise=1 (e o action, quando o anchor traz sa=).

Quanto tempo leva cada resolução na prática?

O reCAPTCHA v2 Enterprise resolve em menos de 60 s. Por isso o exemplo espera 20 s antes da primeira consulta e cobre até 30 tentativas de 5 s antes do timeout.

Posso resolver vários desafios ao mesmo tempo?

Sim. Cada tarefa em andamento ocupa uma thread, então o paralelismo máximo é o número de threads do plano. Dispare os envios com Promise.all e mantenha uma fila na sua aplicação para não passar desse limite.

Por que o site recusa um token que a API marcou como resolvido?

Quase sempre por divergência de User-Agent: o token Enterprise carrega o contexto do navegador que o gerou. Use o user_agent devolvido na consulta e confirme que a pageurl é idêntica à do formulário.

Quais tipos de CAPTCHA esse mesmo fluxo cobre?

O mesmo par in.php / res.php atende as variantes de reCAPTCHA, Cloudflare Turnstile e Challenge, GeeTest v3 e CAPTCHAs de imagem e de grade — muda o method, não a lógica do script. O hCaptcha e o FunCaptcha (Arkose Labs) não são suportados, e o GeeTest v4 aparece apenas como "em breve".


Coloque o fluxo para rodar

Pegue sua chave de API em captchaai.com, acrescente enterprise=1 às requisições v2 e valide o fluxo no seu staging antes de levá-lo ao pipeline.


Guias relacionados

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