Deux appels HTTP suffisent : vous envoyez l'image à l'API OCR de CaptchaAI, puis vous interrogez le résultat jusqu'à récupérer le texte attendu par le formulaire. Une trentaine de lignes avec axios, aucun modèle à entraîner, aucun prétraitement d'image obligatoire.
Au programme : les deux méthodes d'envoi, la boucle d'interrogation, les paramètres qui améliorent la reconnaissance, un script complet avec Puppeteer et les erreurs rencontrées en production.
Ce dont vous avez besoin avant de commencer
| Élément | Valeur |
|---|---|
| Clé API CaptchaAI | Depuis captchaai.com |
| Node.js | 14+ |
| Bibliothèques | axios, fs |
| Format des images | JPG, PNG ou GIF (100 octets – 100 Ko) |
Gardez la clé API hors du dépôt : une variable d'environnement, jamais une chaîne en dur — YOUR_API_KEY n'est ici qu'un repère.
Où ces CAPTCHA en texte déformé apparaissent encore
Le CAPTCHA d'image classique — quelques caractères tordus sur un fond bruité — a quitté les grands sites, mais il reste partout où le logiciel a dix ans : portails administratifs, extranets métier, back-offices de facturation, plateformes B2B régionales, ou les portails de rendez-vous BLS que beaucoup de lecteurs au Maghreb consultent pour leurs propres démarches.
Le point commun de ces surfaces : ni widget moderne, ni token JavaScript à récupérer, seulement une balise <img> et un champ texte. Votre script capture donc l'image, la fait lire, puis saisit le résultat comme le ferait une personne. C'est le rôle de l'endpoint OCR.
Côté conformité : une capture prise sur une page authentifiée peut contenir des données personnelles. Cadrez-la sur le seul défi CAPTCHA, purgez les fichiers temporaires et traitez ce point dans vos obligations RGPD.
Étape 1 : envoyez l'image encodée en base64
La voie la plus directe quand l'image est déjà en mémoire, après un screenshot() Puppeteer par exemple. Vous encodez le fichier et vous postez sur in.php avec method=base64. La réponse JSON porte l'identifiant de tâche dans request quand status vaut 1 ; sinon, request contient le code d'erreur.
const axios = require('axios');
const fs = require('fs');
const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// Read and encode the image
const imageB64 = fs.readFileSync('captcha.png').toString('base64');
// Submit to CaptchaAI
const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: API_KEY,
method: 'base64',
body: imageB64,
json: 1,
},
});
if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);
Conservez cet identifiant : c'est la seule clé qui permet d'aller chercher le texte à l'étape suivante, et de signaler plus tard une lecture incorrecte.
Étape 2 (variante) : envoyez le fichier tel quel
Si l'image arrive d'un téléchargement ou d'un dossier surveillé, l'envoi multipart évite un aller-retour d'encodage. Le principe reste identique, seul le transport change : method=post, un FormData et le flux de lecture du fichier.
const FormData = require('form-data');
const form = new FormData();
form.append('key', API_KEY);
form.append('method', 'post');
form.append('json', '1');
form.append('file', fs.createReadStream('captcha.png'));
const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', form, {
headers: form.getHeaders(),
});
const taskId = submitData.request;
Les deux méthodes alimentent la même file d'attente de résolution. Choisissez selon l'endroit d'où vient l'image, pas selon une hypothèse de vitesse.
Étape 3 : interrogez le résultat jusqu'au texte final
L'API est asynchrone. Après l'envoi, laissez passer cinq secondes, puis interrogez res.php à intervalle régulier. Tant que le travail est en cours, la réponse vaut CAPCHA_NOT_READY : c'est un état normal, pas une erreur. Toute autre valeur inattendue doit interrompre la boucle immédiatement, sinon vous occupez un thread pour rien pendant deux minutes et demie.
await sleep(5000);
let captchaText;
for (let i = 0; i < 30; i++) {
const { data: pollData } = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
});
if (pollData.status === 1) {
captchaText = pollData.request;
console.log(`CAPTCHA text: ${captchaText}`);
break;
}
if (pollData.request !== 'CAPCHA_NOT_READY') {
throw new Error(pollData.request);
}
await sleep(5000);
}
Un intervalle de 5 secondes sur trente tentatives est un bon point de départ. Interroger toutes les secondes n'accélère rien et multiplie les requêtes inutiles.
Étape 4 : cadrez la reconnaissance avec les paramètres de précision
La plupart des lectures ratées ne viennent pas de l'image mais du manque de contexte. Si vous savez que le formulaire attend six chiffres, dites-le : le moteur écarte alors les hypothèses impossibles.
// Digits only, 4-6 characters
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: API_KEY,
method: 'base64',
body: imageB64,
numeric: 1, // digits only
min_len: 4, // minimum length
max_len: 6, // maximum length
json: 1,
},
});
| Paramètre | Valeur | Objectif |
|---|---|---|
numeric |
1 = chiffres, 2 = lettres |
Limite les caractères |
min_len / max_len |
Entier | Contraintes de longueur |
calc |
1 |
Calcule une expression mathématique |
regsense |
1 |
Sensible à la casse |
calc sert aux vieux formulaires affichant « 7 + 4 = ? » en image : vous recevez le résultat calculé. Quand une lecture est fausse, signalez-la via res.php?key=KEY&action=reportbad&id=TASK_ID.
Exemple complet en Node.js : de la capture Puppeteer au formulaire soumis
Ce script enchaîne les quatre étapes précédentes : ouverture de la page, capture de l'élément #captcha-image, envoi, interrogation, saisie et soumission.
const axios = require('axios');
const puppeteer = require('puppeteer');
const fs = require('fs');
const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function solveImageCaptcha() {
// 1. Load page and screenshot CAPTCHA
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/register');
const captchaEl = await page.$('#captcha-image');
await captchaEl.screenshot({ path: 'captcha.png' });
// 2. Encode and submit
const imageB64 = fs.readFileSync('captcha.png').toString('base64');
const { data: submit } = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: { key: API_KEY, method: 'base64', body: imageB64, json: 1 },
});
const taskId = submit.request;
// 3. Poll for text
await sleep(5000);
let text;
for (let i = 0; i < 30; i++) {
const { data: poll } = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
});
if (poll.status === 1) { text = poll.request; break; }
if (poll.request !== 'CAPCHA_NOT_READY') throw new Error(poll.request);
await sleep(5000);
}
// 4. Type and submit
await page.type('#captcha-input', text);
await page.click('form [type="submit"]');
console.log(`Solved: ${text}`);
await browser.close();
}
solveImageCaptcha().catch(console.error);
Résultat attendu :
Solved: ABC123
Encadrez cette fonction d'un try/finally qui ferme toujours le navigateur, et prévoyez une nouvelle tentative si le formulaire refuse le texte : la deuxième image est souvent plus lisible.
Erreurs fréquentes et correctifs
| Erreur | Cause | Correctif |
|---|---|---|
ERROR_WRONG_FILE_EXTENSION |
Format non pris en charge | Utilisez JPG, PNG ou GIF |
ERROR_TOO_BIG_CAPTCHA_FILESIZE |
Image supérieure à 100 Ko | Compressez avant l'envoi |
ERROR_ZERO_CAPTCHA_FILESIZE |
Image inférieure à 100 octets | Vérifiez le sélecteur de capture |
CAPCHA_NOT_READY |
Résolution encore en cours | Interrogez toutes les 5 secondes |
Les deux erreurs de taille se règlent dans le sélecteur : une capture prise sur le conteneur parent gonfle le fichier, une capture prise sur un élément encore masqué produit un fichier vide.
Passer en production : threads, coût et hébergement
La facturation CaptchaAI se fait au thread simultané, pas à la résolution : chaque plan inclut un nombre illimité de résolutions par thread. Un thread correspond à une image en cours de lecture ; dès qu'elle est rendue, il reprend la suivante.
Pour du monitoring de formulaires, BASIC ($15/mois, 5 threads) suffit. Un pipeline de scraping multi-domaines est plus à l'aise sur STANDARD ($30/mois, 15 threads), voire ADVANCE ($90/mois, 50 threads). Facturation en dollars US.
Côté exécution, hébergez le worker près des sites visés : une instance OVHcloud ou Scaleway, ou une région AWS eu-west-3 (Paris), réduit la latence sur des portails européens. Le temps de résolution, lui, dépend de l'image.
Journalisez enfin trois valeurs par tâche : l'identifiant, le nombre d'interrogations et le texte renvoyé. C'est le minimum pour distinguer un problème de lecture d'un problème de formulaire.
FAQ
Quels formats et quelles tailles d'image l'API accepte-t-elle ?
JPG, PNG et GIF, entre 100 octets et 100 Ko. Au-delà, recompressez ou resserrez la capture sur le seul défi CAPTCHA.
Comment améliorer la précision sur des images très bruitées ?
Décrivez ce que vous attendez avec numeric, min_len, max_len et regsense. Une longueur exacte change souvent plus le résultat qu'un prétraitement d'image.
Combien de threads faut-il pour lire plusieurs images en parallèle ?
Autant que d'images traitées simultanément. Cinq threads (BASIC, $15/mois) couvrent cinq lectures en même temps, sans limite de volume mensuel sur ces threads.
Que faire si la boucle atteint ses trente tentatives sans réponse ?
Abandonnez la tâche, recapturez une image et repartez sur un envoi neuf : une tâche anormalement longue ne se débloque pas en étant interrogée davantage.
CaptchaAI prend-il en charge hCaptcha ou FunCaptcha ?
Non — ces deux types ne sont pas pris en charge. L'offre couvre reCAPTCHA v2 et v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3, ainsi que les CAPTCHA image/OCR et les grilles d'images traités ici.
Guides associés
Ouvrez votre compte CaptchaAI et lisez votre première image →