enterprise=1 : ce paramètre est la seule différence entre résoudre un reCAPTCHA v2 standard et un reCAPTCHA v2 Enterprise avec l'API CaptchaAI. Case « Je ne suis pas un robot », grilles d'images, format du token, polling : le reste est identique. Tout se joue donc en amont, sur la détection — et l'erreur ne lève aucune exception : vous obtenez un token bien formé, que le site refuse ensuite sans explication.
Détecter reCAPTCHA v2 Enterprise avant d'envoyer la tâche
Rien à l'écran ne sépare les deux versions. Le seul signal fiable est le fichier JavaScript chargé.
Le src de la balise script
<!-- Standard v2 -->
<script src="https://www.google.com/recaptcha/api.js"></script>
<!-- Enterprise v2 -->
<script src="https://www.google.com/recaptcha/enterprise.js"></script>
La détection automatisée en Python
import requests
from bs4 import BeautifulSoup
def detect_recaptcha_version(url):
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
enterprise_script = soup.find("script", src=lambda s: s and "enterprise.js" in s)
standard_script = soup.find("script", src=lambda s: s and "recaptcha/api.js" in s)
widget = soup.find(class_="g-recaptcha")
sitekey = widget["data-sitekey"] if widget else None
if enterprise_script:
return {"version": "enterprise_v2", "sitekey": sitekey}
elif standard_script:
return {"version": "standard_v2", "sitekey": sitekey}
return None
info = detect_recaptcha_version("https://example.com/login")
print(info)
Le même contrôle en Node.js
const axios = require("axios");
const cheerio = require("cheerio");
async function detectRecaptchaVersion(url) {
const { data } = await axios.get(url);
const $ = cheerio.load(data);
const hasEnterprise = $('script[src*="enterprise.js"]').length > 0;
const hasStandard = $('script[src*="recaptcha/api.js"]').length > 0;
const sitekey = $(".g-recaptcha").attr("data-sitekey");
if (hasEnterprise) return { version: "enterprise_v2", sitekey };
if (hasStandard) return { version: "standard_v2", sitekey };
return null;
}
Le contrôle manuel dans la console DevTools
// Quick check in DevTools
if (document.querySelector('script[src*="enterprise.js"]')) {
console.log("Enterprise v2");
} else if (document.querySelector('script[src*="recaptcha/api.js"]')) {
console.log("Standard v2");
}
- Widget injecté après le chargement : le HTML brut ne contient alors aucun des deux fichiers. Attendez
.g-recaptchaen navigateur headless, puis relisez le DOM. - Journalisez la version détectée : le jour où un site bascule vers
enterprise.js, vos logs vous préviennent avant vos tickets de support.
Ce qui change réellement entre les deux versions
- Côté défi : rien ne bouge, mêmes images, même token.
- Côté éditeur : seuils par action, codes de motif, facturation par projet Google Cloud.
- Côté code client : la seule variable est
enterprise=1, mais la vérification serveur sait quelle clé a produit le token.
| Caractéristique | Standard v2 | Enterprise v2 |
|---|---|---|
| Fichier JS chargé | api.js |
enterprise.js |
| Fonction d'exécution | grecaptcha.execute() |
grecaptcha.enterprise.execute() |
| API de vérification | siteverify (gratuite) |
recaptchaenterprise.googleapis.com (payante) |
| Codes de motif | Non | Oui (AUTOMATION, TOO_MUCH_TRAFFIC, etc.) |
| Règles personnalisées | Non | Oui (seuils par action) |
| Google Cloud Console | Non | Oui (gestion par projet) |
| Paramètre CaptchaAI | — | enterprise=1 |
| Temps de résolution | < 60 s | < 60 s |
Résoudre les deux versions avec l'API CaptchaAI
Standard v2 : la requête de référence
import requests
import time
# Submit task
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
# Poll for token
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
Enterprise v2 : un paramètre de plus
import requests
import time
# Submit task — only difference is enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"enterprise": 1 # Required for Enterprise
})
task_id = resp.text.split("|")[1]
# Polling is identical
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
Endpoint, méthode et token renvoyé ne bougent pas : maintenir deux chemins de code séparés ne crée que des occasions de divergence.
Une classe unique qui choisit toute seule
import requests
import time
from bs4 import BeautifulSoup
class RecaptchaV2Solver:
def __init__(self, api_key):
self.api_key = api_key
def detect_and_solve(self, page_url, page_html=None):
if not page_html:
page_html = requests.get(page_url).text
soup = BeautifulSoup(page_html, "html.parser")
is_enterprise = bool(soup.find("script", src=lambda s: s and "enterprise.js" in s))
widget = soup.find(class_="g-recaptcha")
sitekey = widget["data-sitekey"] if widget else None
if not sitekey:
raise Exception("No reCAPTCHA sitekey found on page")
params = {
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"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 {
"token": result.text.split("|")[1],
"is_enterprise": is_enterprise,
"sitekey": sitekey
}
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve failed: {result.text}")
raise Exception("Solve timed out")
solver = RecaptchaV2Solver("YOUR_API_KEY")
result = solver.detect_and_solve("https://example.com/login")
print(f"Enterprise: {result['is_enterprise']}, Token: {result['token'][:40]}...")
Un seul chemin de code, donc un seul endroit à corriger. Traitez CAPCHA_NOT_READY comme une attente normale, tout autre retour comme une erreur.
Injecter le token : rigoureusement identique
Le champ cible reste g-recaptcha-response, et le callback se déclenche de la même façon dans les deux cas.
# Selenium injection — works for both standard and enterprise
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# If the page uses a callback function
callback = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
if callback:
driver.execute_script(f'{callback}("{token}");')
// Puppeteer injection — works for both
await page.evaluate((token) => {
document.getElementById("g-recaptcha-response").value = token;
// Find and call callback if present
const widget = document.querySelector(".g-recaptcha");
const cb = widget?.getAttribute("data-callback");
if (cb && typeof window[cb] === "function") {
window[cb](token);
}
}, token);
Pièges fréquents et correctifs
enterprise=1envoyé à une page standard. Le token revient bien formé, mais peut être invalide à la vérification. Confirmezenterprise.jsavant d'ajouter le paramètre.enterprise=1oublié sur une page Enterprise. Le backend du site rejette le token.- Mauvais sitekey transmis. Vous recevez
ERROR_WRONG_GOOGLEKEY: relisezdata-sitekeydans le DOM. - v2 Enterprise confondue avec v3 Enterprise. La v2 affiche une case à cocher, la v3 reste invisible : les paramètres de la tâche diffèrent.
- Script injecté après le chargement. Aucun fichier reCAPTCHA dans le HTML : passez par un navigateur headless.
Budget et capacité : ce que la version ne change pas
Cas concret : une équipe QA à Lyon valide chaque nuit un tunnel d'inscription hébergé chez OVHcloud, workers en eu-west-3 (Paris). Le staging est en reCAPTCHA v2 standard, la préproduction du partenaire en Enterprise. La facturation CaptchaAI se fait en dollars US et repose sur les threads — les résolutions simultanées — jamais sur le type de widget.
- BASIC ($15/mois, 5 threads) suffit à des tests nocturnes.
- STANDARD ($30/mois, 15 threads) couvre plusieurs environnements en parallèle.
- ADVANCE ($90/mois, 50 threads) vise les exécutions continues.
Un défi occupe un thread puis le libère : dimensionnez sur le pic simultané, pas sur le total quotidien. Côté RGPD, ne consignez aucune donnée personnelle issue des formulaires testés.
FAQ
Le sitekey change-t-il quand un site passe en reCAPTCHA v2 Enterprise ?
Le plus souvent oui : la clé Enterprise vit dans un projet Google Cloud distinct. Relisez-la dans le DOM à chaque exécution.
Un token accepté par CaptchaAI est refusé par le site : que vérifier ?
Le paramètre enterprise d'abord : un token obtenu sans lui sur une page enterprise.js, ou l'inverse, échoue à la vérification serveur. Contrôlez ensuite le pageurl.
reCAPTCHA v2 Enterprise et reCAPTCHA v3 Enterprise, est-ce la même intégration ?
Non. La v2 Enterprise reste un défi visible ; la v3 Enterprise s'exécute sans interaction et renvoie un score lié à une action.
CaptchaAI prend-il en charge hCaptcha pour les sites qui ont migré ?
Non — hCaptcha n'est pas pris en charge, pas plus que FunCaptcha (Arkose Labs) ; GeeTest v4 est à venir. Sont couverts : reCAPTCHA v2 et v3, Cloudflare Turnstile et Challenge, GeeTest v3, les CAPTCHA image/texte, plus CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta).