Comparisons

reCAPTCHA v3 padrão vs Enterprise: como identificar e resolver

Seu script resolve reCAPTCHA v3 há meses e, de repente, o formulário recusa todos os tokens. A causa costuma ser uma só: a página trocou o v3 padrão pelo reCAPTCHA Enterprise. Para quem integra a resolução, a diferença cabe em um parâmetro — enterprise=1. O difícil é perceber a troca: ambas rodam de forma invisível, devolvem a mesma pontuação de risco (0,0 a 1,0) e não mudam nada na tela. A seguir, a detecção e o código para as duas versões.

Como descobrir qual versão a página usa

A checagem é no JavaScript, nunca no visual:

  • api.js?render=SITEKEY com grecaptcha.execute() — v3 padrão
  • enterprise.js?render=SITEKEY com grecaptcha.enterprise.execute() — v3 Enterprise

Rode a detecção a cada carregamento de página.

Detecção em Python

import requests
import re

def detect_v3_type(url):
    resp = requests.get(url)
    html = resp.text

    # Check for enterprise.js
    if "enterprise.js" in html:
        version = "enterprise_v3"
        execute_fn = "grecaptcha.enterprise.execute"
    elif "recaptcha/api.js" in html and "render=" in html:
        version = "standard_v3"
        execute_fn = "grecaptcha.execute"
    else:
        return None

    # Extract sitekey from render parameter
    key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
    sitekey = key_match.group(1) if key_match else None

    # Extract action parameter
    action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
    action = action_match.group(1) if action_match else "unknown"

    return {
        "version": version,
        "sitekey": sitekey,
        "action": action,
        "execute_fn": execute_fn
    }

info = detect_v3_type("https://staging.example.com/qa-login")
print(info)

Detecção em Node.js

const axios = require("axios");

async function detectV3Type(url) {
  const { data: html } = await axios.get(url);

  let version, executeFn;
  if (html.includes("enterprise.js")) {
    version = "enterprise_v3";
    executeFn = "grecaptcha.enterprise.execute";
  } else if (html.includes("recaptcha/api.js") && html.includes("render=")) {
    version = "standard_v3";
    executeFn = "grecaptcha.execute";
  } else {
    return null;
  }

  const keyMatch = html.match(/render[=:]\s*['"]?([A-Za-z0-9_-]{40})/);
  const actionMatch = html.match(/action['"]?\s*[:=]\s*['"](\w+)/);

  return {
    version,
    sitekey: keyMatch?.[1] || null,
    action: actionMatch?.[1] || "unknown",
    executeFn,
  };
}

Checagem rápida no console do navegador

Para uma resposta imediata, cole isto no DevTools da página:

// Paste in DevTools console
if (document.querySelector('script[src*="enterprise.js"]')) {
  console.log("Enterprise v3");
  console.log("Execute:", typeof grecaptcha?.enterprise?.execute);
} else if (document.querySelector('script[src*="api.js"][src*="render="]')) {
  console.log("Standard v3");
  console.log("Execute:", typeof grecaptcha?.execute);
}

Padrão e Enterprise lado a lado

Recurso v3 padrão v3 Enterprise
Operação invisível Sim Sim
Pontuação de risco (0,0–1,0) Sim Sim
Parâmetro action Obrigatório Obrigatório
Arquivo JavaScript api.js?render=KEY enterprise.js?render=KEY
Função de execução grecaptcha.execute() grecaptcha.enterprise.execute()
Reason codes Não Sim (AUTOMATION, TOO_MUCH_TRAFFIC)
Limite por ação Não Sim (Google Cloud Console)
Detecção de vazamento de senha Não Sim
Account Defender Não Sim
Endpoint de verificação siteverify (grátis) recaptchaenterprise.googleapis.com (pago)
Parâmetros na CaptchaAI version=v3 version=v3 + enterprise=1
Tempo de resolução na CaptchaAI < 4 s < 4 s

Resolvendo as duas versões com a CaptchaAI

O fluxo não muda: envie a tarefa para in.php, consulte o resultado em res.php e mande o token com o formulário.

v3 padrão

import requests
import time

# Submit
resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "version": "v3",
    "googlekey": sitekey,
    "action": "login",
    "pageurl": page_url
})
task_id = resp.text.split("|")[1]

# Poll
for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

v3 Enterprise: um parâmetro a mais

A única alteração é enterprise: 1 no envio; a consulta de resultado é idêntica.

import requests
import time

# Submit — add enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "version": "v3",
    "enterprise": 1,  # Required for Enterprise
    "googlekey": sitekey,
    "action": "login",
    "pageurl": page_url
})
task_id = resp.text.split("|")[1]

# Polling is identical to standard
for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

Um solver único, com detecção automática

Manter dois caminhos separados gera esquecimento. Concentre detecção e resolução em uma classe só:

import requests
import time
import re

class RecaptchaV3Solver:
    def __init__(self, api_key):
        self.api_key = api_key

    def detect_and_solve(self, page_url, action=None):
        """Auto-detect standard vs enterprise and solve."""
        html = requests.get(page_url).text

        is_enterprise = "enterprise.js" in html
        key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
        if not key_match:
            raise Exception("No v3 sitekey found")
        sitekey = key_match.group(1)

        if not action:
            action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
            action = action_match.group(1) if action_match else "verify"

        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "version": "v3",
            "googlekey": sitekey,
            "action": action,
            "pageurl": page_url
        }
        if is_enterprise:
            params["enterprise"] = 1

        resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit failed: {resp.text}")

        task_id = resp.text.split("|")[1]
        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/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":
                raise Exception(f"Solve failed: {result.text}")

        raise Exception("Timed out")

solver = RecaptchaV3Solver("YOUR_API_KEY")
token = solver.detect_and_solve("https://staging.example.com/qa-login", action="login")
print(f"Token: {token[:40]}...")

Como extrair o parâmetro action correto

O action vale para as duas versões e é o detalhe que mais pesa na pontuação: um valor diferente do declarado pela página invalida o token. Extraia a string do JavaScript:

import re

def find_v3_actions(html):
    """Extract all action parameters from page JavaScript."""
    # Look for grecaptcha.execute(key, {action: '...'})
    pattern = r"(?:grecaptcha\.(?:enterprise\.)?execute|action)\s*[(:]\s*['\"](\w+)"
    actions = re.findall(pattern, html)
    return list(set(actions))

# Common actions: "login", "submit", "register", "checkout", "homepage"

Os valores mais comuns são login, submit, register, checkout e homepage.

Onde entra o token depois da resolução

O campo é o mesmo nas duas versões, g-recaptcha-response: com navegador, preencha antes do envio; em HTTP puro, mande-o com os dados do formulário.

# For browser-based workflows (Selenium)
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# For pure HTTP workflows
requests.post(page_url, data={
    "g-recaptcha-response": token,
    "username": "user",
    "password": "pass"
})
// Puppeteer
await page.evaluate((tok) => {
  document.getElementById("g-recaptcha-response").value = tok;
}, token);

// Pure HTTP (axios)
await axios.post(pageUrl, new URLSearchParams({
  "g-recaptcha-response": token,
  username: "user",
  password: "pass",
}));

Uma checagem em tempo de execução no Selenium

Se o fluxo já abre o navegador, confirme a versão lendo o HTML que o driver já tem:

page_source = driver.page_source
is_enterprise = "enterprise.js" in page_source

Erros que mais aparecem em produção

Erro O que acontece Correção
enterprise=1 em página v3 padrão Token pode ser recusado Confirme enterprise.js antes de ligar
Sem enterprise=1 em página Enterprise O backend recusa o token Ligue sempre que enterprise.js aparecer
action errado Pontuação baixa, token recusado Extraia a string exata do JavaScript
Sem version=v3 O solver trata a tarefa como v2 Inclua version=v3 em reCAPTCHA por pontuação
Sitekey de v2 na resolução v3 ERROR_WRONG_GOOGLEKEY A sitekey de v3 vem do render=KEY

Cenário: dois ambientes de staging no mesmo pipeline

Um time de e-commerce em São Paulo mantém dois ambientes de staging: um com v3 padrão, outro já migrado para Enterprise. Com a versão fixa no código, metade da suíte de regressão do checkout quebrava a cada deploy — e o sintoma aparecia como token recusado, não como erro de configuração. Com detecção por carregamento, o mesmo teste atende os dois.

Dois detalhes locais: workers em sa-east-1 reduzem o RTT até o formulário em teste; e a suíte usa dados fictícios em staging.example.com — quando a automação toca dados reais de clientes, as obrigações da LGPD entram na conversa.

Perguntas frequentes

Preciso de conta no Google Cloud para resolver o Enterprise?

Não. O projeto no Google Cloud é de quem opera o site. Para resolver, bastam a sitekey pública, a URL da página e o action.

O action muda quando o site é Enterprise?

O papel é o mesmo, mas o valor é escolhido por quem desenvolveu a página e pode variar entre ambientes. Extraia do HTML a cada execução.

Quantas threads eu preciso para rodar detecção e resolução em paralelo?

Conte quantos desafios você quer resolver ao mesmo tempo, não quantos por dia — as resoluções por thread são ilimitadas. Uma suíte sequencial cabe no BASIC (US$ 15/mês, 5 threads); regressão paralela costuma pedir o STANDARD (US$ 30/mês, 15 threads).

O token voltou, mas o formulário continua recusando. O que verificar?

Nesta ordem: o enterprise combina com o arquivo JS; o action é idêntico ao declarado; a sitekey veio do render=; e o token foi enviado na mesma sessão que carregou a página. Token de v3 vale pouco tempo — se ficou parado em fila, resolva de novo.

Guias relacionados

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