Votre script remplit le formulaire, clique sur « Envoyer », et la page revient à son état initial sans message d'erreur : c'est très souvent un reCAPTCHA v2 Invisible qui vient de refuser la soumission. Cette variante n'affiche aucune case à cocher, seulement un badge discret dans un coin. Elle se déclenche au clic ou à la soumission, et n'ouvre une grille d'images que si l'analyse de risque de Google juge la session douteuse. Repérer sa présence avant d'écrire l'intégration vous épargne des heures de débogage sur un formulaire qui semble fonctionner.
Les quatre contrôles ci-dessous prennent moins d'une minute et suffisent à trancher.
Repérer un reCAPTCHA v2 Invisible dans le DOM
Ouvrez la console sur la page cible et exécutez-les dans l'ordre, du plus rapide au plus fouillé.
1. Lire l'attribut data-size
Le signal le plus fiable reste l'attribut data-size du widget. La valeur invisible ne laisse aucune ambiguïté.
// Browser console
const widgets = document.querySelectorAll('.g-recaptcha');
widgets.forEach((el, i) => {
const size = el.getAttribute('data-size');
const sitekey = el.getAttribute('data-sitekey');
console.log(`Widget ${i}: size=${size}, sitekey=${sitekey}`);
if (size === 'invisible') {
console.log(' → This is Invisible reCAPTCHA');
}
});
Notez le sitekey affiché : avec l'URL de la page, c'est tout ce dont l'API a besoin.
2. Chercher le badge grecaptcha-badge
Quand le widget est instancié par script, le div .g-recaptcha peut être absent alors que le badge, lui, est bien injecté.
const badge = document.querySelector('.grecaptcha-badge');
if (badge) {
console.log('reCAPTCHA badge found — likely Invisible reCAPTCHA');
console.log('Badge visibility:', getComputedStyle(badge).visibility);
}
Un badge masqué en CSS (visibility: hidden) ne désactive rien : la vérification reste active côté serveur.
3. Traquer les appels grecaptcha.execute
Sur les intégrations programmatiques, le sitekey se cache dans un script inline plutôt que dans un attribut HTML.
// Look for grecaptcha.execute in page scripts
document.querySelectorAll('script:not([src])').forEach(s => {
if (s.textContent.includes('grecaptcha.execute')) {
console.log('Found grecaptcha.execute — Invisible reCAPTCHA');
const match = s.textContent.match(/grecaptcha\.execute\s*\(\s*['"]?([^'",\s)]+)/);
if (match) console.log('Sitekey:', match[1]);
}
});
4. Inspecter le paramètre render du script
Dernier filet : l'URL de chargement de l'API Google. Un render= suivi d'une clé indique une invocation automatique, donc une variante invisible ou une v3.
document.querySelectorAll('script[src*="recaptcha"]').forEach(s => {
if (s.src.includes('render=') && !s.src.includes('render=explicit')) {
console.log('Invisible/v3 reCAPTCHA detected in script:', s.src);
}
});
Ce qui change par rapport à la case à cocher
| Caractéristique | reCAPTCHA v2 standard | reCAPTCHA v2 Invisible |
|---|---|---|
| Widget visible | Oui (case à cocher) | Non (badge seul) |
| Action du visiteur | Clic sur la case | Automatique à la soumission |
data-size |
normal ou compact |
invisible |
| Ouverture d'un défi | Toujours possible | Uniquement pour les sessions jugées suspectes |
| Élément dans le DOM | Div .g-recaptcha |
Div .g-recaptcha ou instanciation par script |
Conséquence pratique : un test manuel ne prouve rien. Votre navigateur habituel passera sans défi visible là où un worker hébergé en datacenter déclenchera la grille d'images.
Envoyer la tâche à CaptchaAI avec invisible=1
Le paramètre qui fait toute la différence s'appelle invisible. Il indique au service qu'il traite la variante invisible ; le reste de l'appel est identique à un reCAPTCHA v2 classique, avec la même méthode userrecaptcha et les mêmes endpoints in.php et res.php.
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
# Submit with invisible flag
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "6Le-SITEKEY",
"pageurl": "https://example.com/login",
"invisible": "1", # critical for Invisible reCAPTCHA
"json": "1",
}).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
print(f"Submitted: {task_id}")
# Poll for result
for _ in range(24):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if result["status"] == 1:
print(f"Token: {result['request'][:50]}...")
break
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
Cinq secondes entre deux interrogations est un bon compromis : plus court, vous consommez des requêtes pour rien ; plus long, vous ajoutez du délai.
JavaScript
const axios = require('axios');
const resp = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: 'YOUR_API_KEY',
method: 'userrecaptcha',
googlekey: '6Le-SITEKEY',
pageurl: 'https://example.com/login',
invisible: 1,
json: 1,
}
});
const taskId = resp.data.request;
console.log(`Submitted: ${taskId}`);
Injecter le token puis déclencher le callback
C'est l'étape où la plupart des intégrations échouent. Renseigner le champ ne suffit pas : la variante invisible attend aussi l'appel de sa fonction de validation, sans quoi le formulaire part avec un état interne incohérent et le serveur rejette la requête. Deux gestes, dans cet ordre :
- écrire le token dans le
textareag-recaptcha-response; - appeler la fonction déclarée dans
data-callbackavec ce même token.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# After solving, inject and trigger
driver.execute_script("""
// Set the token
document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
// Find and trigger the callback
var widget = document.querySelector('.g-recaptcha');
var callbackName = widget ? widget.getAttribute('data-callback') : null;
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](arguments[0]);
}
""", token)
# Submit the form
driver.find_element(By.CSS_SELECTOR, "form#login").submit()
Sans data-callback déclaré, la page utilise en général une soumission directe : renseignez le champ, puis appelez submit().
Identifier l'élément déclencheur
Le défi peut être rattaché à un bouton précis, avec toute la configuration portée par ses attributs :
<button class="g-recaptcha"
data-sitekey="6Le-SITEKEY"
data-callback="onSubmit"
data-size="invisible">
Submit
</button>
Ou déclenché par le code du site, ce qui rend la détection statique inopérante :
// Site's code
document.getElementById('submit-btn').addEventListener('click', function() {
grecaptcha.execute();
});
Balayez les deux cas avant de conclure :
// Find elements with g-recaptcha class that are buttons
document.querySelectorAll('button.g-recaptcha, input.g-recaptcha').forEach(el => {
console.log('Trigger element:', el.tagName, el.textContent.trim());
console.log(' data-sitekey:', el.getAttribute('data-sitekey'));
console.log(' data-callback:', el.getAttribute('data-callback'));
});
Cas concret : le formulaire de devis d'un éditeur SaaS
Une équipe QA lyonnaise surveille le formulaire de devis de son propre site, hébergé en Europe, via un scénario nocturne exécuté depuis la région AWS de Paris (eu-west-3). Vert pendant des semaines, il s'est mis à remonter des soumissions « réussies » sans qu'aucun devis n'apparaisse en base.
Trois minutes de console ont suffi : pas de div .g-recaptcha, mais un badge présent et un grecaptcha.execute() appelé depuis le gestionnaire de clic du bouton. L'équipe a ajouté invisible: "1" à l'appel CaptchaAI, récupéré la fonction déclarée dans data-callback, puis relancé la campagne : les devis sont réapparus dès la nuit suivante.
Deux réflexes : ne testez que des environnements dont vous êtes responsable, et limitez les données personnelles de vos jeux d'essai — mille formulaires par nuit avec de vraies adresses e-mail relèvent du RGPD. Côté capacité, ce scénario tient dans le plan BASIC ($15/mois, 5 threads).
Dépannage
| Symptôme | Cause probable | Correctif |
|---|---|---|
| Token accepté par l'API mais refusé par le site | invisible=1 absent de la soumission |
Ajoutez invisible: "1" à la requête CaptchaAI |
| Sitekey introuvable | Aucun div .g-recaptcha dans la page |
Cherchez les appels grecaptcha.render() ou grecaptcha.execute() dans les scripts inline |
| Le formulaire part mais la validation échoue | Callback jamais appelé | Récupérez la valeur de data-callback et invoquez la fonction avec le token |
| Aucun CAPTCHA visible pendant les tests manuels | Le défi ne s'ouvre que pour les sessions jugées suspectes | Vérifiez la présence de grecaptcha-badge ou des balises de script reCAPTCHA |
CAPCHA_NOT_READY en boucle |
Interrogation du résultat trop rapprochée après l'envoi | Laissez cinq secondes entre deux appels à res.php et prévoyez un timeout global |
FAQ
Le badge reCAPTCHA est masqué par le CSS : le défi est-il désactivé ?
Non. Masquer le badge est une pratique courante — Google l'autorise si la mention légale figure ailleurs sur la page — mais la vérification du token côté serveur reste active. Considérez la page comme protégée tant qu'une soumission n'aboutit pas sans token.
Comment distinguer rapidement la variante invisible d'un reCAPTCHA v3 ?
Regardez l'appel : la v2 Invisible utilise grecaptcha.execute() sans paramètre action, alors que la v3 transmet une action et charge son script avec render=SITEKEY. La v3 renvoie en outre un score et n'affiche jamais de grille.
Que faire si l'attribut data-callback est absent ?
Injectez le token dans le champ g-recaptcha-response, puis soumettez le formulaire directement. Si la validation échoue, inspectez le gestionnaire d'événement du bouton : la fonction de rappel y est parfois définie en closure, sans attribut HTML.
Combien de threads faut-il prévoir pour ce type de scénario ?
Un thread correspond à une résolution en cours, pas à un quota mensuel. Comptez vos soumissions simultanées, pas votre volume quotidien : cinq threads absorbent un scénario QA séquentiel, et vous changez de palier lorsque les envois se chevauchent durablement.
Passez de la détection à l'intégration
Obtenez votre clé API sur captchaai.com, envoyez une première tâche avec invisible=1, puis comparez le token obtenu au comportement réel du formulaire.