API Tutorials

Comment résoudre reCAPTCHA v2 Enterprise avec Node.js

Le site que vous automatisez est passé de reCAPTCHA v2 à la variante Enterprise, et depuis, le formulaire refuse le token que votre code Node.js lui envoie. Le correctif tient en un paramètre : ajoutez enterprise: "1" à votre appel in.php, et reprenez l'action si l'URL d'ancrage en contient une.

Le widget est identique — la même case « Je ne suis pas un robot » — mais la vérification passe par le backend Enterprise de Google, avec une notation des risques plus stricte. Au programme : reconnaître Enterprise dans DevTools, envoyer la tâche, interroger le résultat, injecter le token, puis dimensionner vos threads.

Ce que change Enterprise pour votre code

Trois différences expliquent les échecs les plus fréquents :

  • enterprise=1 : sans ce paramètre, l'API traite la tâche comme un v2 standard et le token ne passe pas la vérification côté serveur.
  • L'action : Enterprise attache souvent une étiquette (LOGIN, CHECKOUT, SIGNUP) au widget ; si elle figure dans l'URL d'ancrage, elle doit accompagner la requête.
  • Le user_agent : la réponse de résolution peut en renvoyer un. Réutilisez-le pour poster le formulaire, sinon le site voit deux environnements pour un même token.

Le reste du workflow est identique à celui d'un reCAPTCHA v2 : method=userrecaptcha, envoi sur in.php, interrogation sur res.php, champ g-recaptcha-response à l'arrivée.

Prérequis

Élément Détail
Clé API CaptchaAI votre tableau de bord CaptchaAI
Node.js 14+ Avec fetch natif ou node-fetch
Sitekey Paramètre k= de l'URL d'ancrage Enterprise
URL de la page URL complète où le défi CAPTCHA s'affiche
Action (facultatif) Paramètre sa= de l'URL d'ancrage

Étape 1 : repérer un widget reCAPTCHA v2 Enterprise

Ouvrez DevTools, onglet Réseau, rechargez et cherchez la requête d'ancrage :

https://www.google.com/recaptcha/enterprise/anchor?ar=1&k=6LdxxXXxAAAAAAcX...&sa=LOGIN&...

Trois signaux ne trompent pas :

  • le script pointe vers /recaptcha/enterprise.js ou /enterprise/anchor ;
  • k= contient la sitekey ;
  • sa=, quand il est présent, contient l'action.

Un v2 standard, lui, charge /recaptcha/api2/anchor. Dans ce cas, n'ajoutez surtout pas enterprise=1 : la requête se fait sans ce paramètre.

Étape 2 : envoyer la tâche à l'API CaptchaAI

La fonction ci-dessous n'ajoute action que si vous en avez extrait une, et renvoie l'identifiant de tâche.

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;
}

Gardez json: "1" : la réponse en texte brut complique la gestion des erreurs.

Étape 3 : interroger le résultat sans saturer l'API

Laissez 20 secondes au premier appel, puis interrogez res.php toutes les 5 secondes. Un polling plus agressif ne raccourcit pas la résolution.

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");
}

CAPCHA_NOT_READY (orthographe historique de l'API) signifie « continuez d'attendre ». Tout autre code est définitif : arrêtez la boucle et journalisez.

Étape 4 : injecter le token dans le formulaire

Le token part sous le nom g-recaptcha-response, comme pour un v2 standard. Si l'API a renvoyé un user_agent, propagez-le dans vos en-têtes.

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;
}

En navigateur piloté (Puppeteer, Playwright), écrivez la valeur dans le champ caché g-recaptcha-response, puis déclenchez la soumission.

Le script Node.js complet

const API_KEY = "YOUR_API_KEY";
const SITE_KEY = "6LdxxXXxAAAAAAcXxxXxxX91xxxxxxxx8xxOx7A";
const PAGE_URL = "https://example.com/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}`);
})();

Résultat attendu :

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

Codes d'erreur et correctifs

Erreur Cause Correctif
ERROR_WRONG_USER_KEY Format de clé API invalide Vérifiez que la clé fait bien 32 caractères, copiée depuis le tableau de bord
ERROR_KEY_DOES_NOT_EXIST Clé inconnue du service Régénérez la clé depuis votre espace CaptchaAI
ERROR_ZERO_BALANCE Solde épuisé Rechargez le compte avant de relancer le lot
ERROR_BAD_TOKEN_OR_PAGEURL Sitekey ou URL de page erronée Reprenez la valeur k= dans l'URL d'ancrage Enterprise, et l'URL exacte de la page
ERROR_CAPTCHA_UNSOLVABLE Résolution impossible Confirmez qu'il s'agit bien d'un Enterprise v2, puis relancez avec un backoff exponentiel
Token refusé par le site User-Agent incohérent Renvoyez le user_agent fourni dans la réponse de résolution

Une erreur ERROR_BAD_TOKEN_OR_PAGEURL répétée après un changement de fournisseur mérite une contre-vérification : le widget est peut-être passé à Cloudflare Turnstile, autre méthode API.

Dimensionner vos threads : un exemple concret

Cas courant : un worker Node.js hébergé chez OVHcloud ou Scaleway à Paris valide chaque nuit les parcours de connexion d'un portail client protégé par reCAPTCHA v2 Enterprise, soit 2 000 vérifications entre 2 h et 5 h.

La facturation CaptchaAI se fait au thread simultané, pas à la résolution : chaque plan inclut des résolutions illimitées par thread, et un thread enchaîne dès que la résolution en cours se termine. Les durées ci-dessous sont des mesures observées, variables selon l'environnement et l'heure.

  • Le SLA pour reCAPTCHA v2 Enterprise est de < 60 s, les temps observés se situant le plus souvent entre 15 et 30 s.
  • À 30 s par résolution, un thread traite environ 120 résolutions par heure.
  • BASIC ($15/mois, 5 threads) couvre donc de l'ordre de 600 résolutions par heure : les 2 000 vérifications tiennent dans la fenêtre, sans marge.
  • STANDARD ($30/mois, 15 threads) ramène la même charge sous l'heure et absorbe les reprises.
  • Pour un pipeline continu 24 h/24, visez ADVANCE ($90/mois, 50 threads).

Côté conformité, si votre worker journalise les formulaires soumis, appliquez le réflexe RGPD : pas d'identifiants ni de données personnelles dans les logs, seuls l'identifiant de tâche et le code de retour servent au diagnostic.

FAQ

Combien de threads faut-il pour 1 000 résolutions par heure ?

Environ 10 threads à 30 s par résolution, soit le plan STANDARD ($30/mois, 15 threads) avec de la marge. Mesurez votre médiane réelle avant de monter de palier.

enterprise=1 fonctionne-t-il aussi pour reCAPTCHA v3 Enterprise ?

Non, ce sont deux configurations distinctes : reCAPTCHA v3 Enterprise s'envoie avec ses propres paramètres, dont le score minimal attendu. Le enterprise de cette page vise la variante v2 à case à cocher.

Faut-il passer un proxy pour que le token soit accepté ?

Ce n'est pas obligatoire dans la majorité des cas. Ajoutez un proxy résidentiel quand le site associe la validation à l'adresse IP ayant chargé le widget ; sinon, l'alignement du user_agent suffit généralement.

CaptchaAI prend-il en charge hCaptcha si le site bascule dessus ?

Non — hCaptcha et FunCaptcha (Arkose Labs) ne sont pas pris en charge. Les types couverts sont reCAPTCHA v2 et v3 (Enterprise inclus), Cloudflare Turnstile et Challenge, GeeTest v3, les CAPTCHA image/OCR et les grilles d'images ; CaptchaFox, Friendly Captcha et Lemin sont en bêta.

Que faire si la boucle d'interrogation expire ?

Traitez l'expiration comme une tâche perdue : journalisez l'identifiant, appliquez un backoff exponentiel et renvoyez une nouvelle tâche plutôt que de prolonger le polling. Une expiration récurrente trahit le plus souvent une sitekey obsolète.

Passez à la pratique

Récupérez votre clé API sur captchaai.com, ajoutez enterprise=1 à votre requête v2 et faites tourner le script ci-dessus sur une page de test avant de le brancher à votre pipeline.

Guides associés

Les commentaires sont désactivés pour cet article.