Vous voulez des résolutions plus rapides ? Le poste le plus rentable n'est pas le solveur, mais l'attente autour. Une résolution additionne quatre temps, dont trois se réduisent côté client.
- Envoi de la tâche — la requête vers
in.php. - Attente en file — avant qu'un solveur prenne la tâche.
- Exécution du solveur — le seul poste hors de votre contrôle.
- Récupération — le polling ou le callback qui rapatrie le token.
Les réglages ci-dessous couvrent le reste, en Python et Node.js.
Latence de référence par type de CAPTCHA
Les chiffres ci-dessous reposent sur des mesures observées et des retours d'utilisateurs. Les résultats varient selon l'environnement, le volume et le moment de la journée.
| Type de CAPTCHA | Plafond SLA | Meilleur réalisable |
|---|---|---|
| Image/OCR | < 0,5 s | 0,2–0,4 s |
| reCAPTCHA v2 | < 60 s | 10–20 s |
| reCAPTCHA v3 | < 4 s | 1–3 s |
| Cloudflare Turnstile | < 10 s | 4–8 s |
| GeeTest v3 | < 12 s | 6–10 s |
Optimisez d'abord les types lents comme reCAPTCHA v2 ; sur un CAPTCHA image la marge reste minime.
1. Régler l'intervalle de polling
L'intervalle par défaut de 5 secondes gaspille du temps quand la résolution se termine entre deux interrogations. Interrogez vite, puis espacez.
Python
import time
import requests
API_KEY = "YOUR_API_KEY"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def adaptive_poll(task_id, timeout=120):
"""Start polling at 3s, increase to 5s after 4 polls."""
start = time.time()
interval = 3 # start aggressive
polls = 0
while time.time() - start < timeout:
time.sleep(interval)
polls += 1
resp = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1"
}).json()
if resp["status"] == 1:
elapsed = time.time() - start
print(f"Solved in {elapsed:.1f}s ({polls} polls)")
return resp["request"]
if resp["request"] != "CAPCHA_NOT_READY":
raise Exception(resp["request"])
# Back off after initial fast polls
if polls >= 4:
interval = 5
raise TimeoutError(f"Task {task_id} timed out")
JavaScript
async function adaptivePoll(taskId, apiKey, timeout = 120000) {
const start = Date.now();
let interval = 3000;
let polls = 0;
while (Date.now() - start < timeout) {
await new Promise(r => setTimeout(r, interval));
polls++;
const resp = await fetch(
`https://ocr.captchaai.com/res.php?key=${apiKey}&action=get&id=${taskId}&json=1`
);
const data = await resp.json();
if (data.status === 1) {
console.log(`Solved in ${((Date.now() - start) / 1000).toFixed(1)}s (${polls} polls)`);
return data.request;
}
if (data.request !== 'CAPCHA_NOT_READY') {
throw new Error(data.request);
}
if (polls >= 4) interval = 5000;
}
throw new Error(`Task ${taskId} timed out`);
}
Gain : 1 à 4 secondes contre un intervalle fixe. Restez au-dessus de 3 secondes pour éviter le rate limiting.
2. Réutiliser les connexions HTTP (pooling)
Rouvrir une connexion à chaque appel coûte une négociation TCP/TLS. Réutilisez une connexion persistante.
Python
session = requests.Session()
# Use session.get() and session.post() instead of requests.get/post
# The session reuses TCP connections automatically
JavaScript (Node.js)
const { Agent } = require('http');
const axios = require('axios');
const client = axios.create({
httpAgent: new Agent({ keepAlive: true, maxSockets: 10 }),
timeout: 10000,
});
// Use client.get() and client.post() for all API calls
Économie : 50 à 100 ms par requête, surtout quand le worker est loin du point d'entrée — un runner sur eu-west-3 (Paris) ou Scaleway multiplie les appels par résolution.
3. Anticiper la soumission des CAPTCHA (prefetch)
Soumettez la tâche de la page N+1 pendant que le scraper traite la page N.
from concurrent.futures import ThreadPoolExecutor
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
def prefetch_submit(sitekey, page_url):
resp = session.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
})
data = resp.json()
if data["status"] == 1:
return data["request"]
raise Exception(data["request"])
# Submit next page's CAPTCHA while processing current page
with ThreadPoolExecutor(max_workers=2) as pool:
# Submit CAPTCHA for page 2 while processing page 1
future_task = pool.submit(prefetch_submit, "6Le-SITEKEY", "https://example.com/page/2")
# Process page 1...
process_page(current_data)
# Now get the pre-submitted task ID and poll
task_id = future_task.result()
token = adaptive_poll(task_id)
Vous ne stockez aucun jeton : la résolution chevauche simplement le traitement.
4. Choisir la bonne méthode de résolution
Une variante plus rapide existe parfois ; évitez la méthode lente par habitude.
| Scénario | Méthode plus lente | Alternative plus rapide |
|---|---|---|
| reCAPTCHA v2 avec callback connu | userrecaptcha + polling |
userrecaptcha avec pingback (URL de callback) |
| CAPTCHA image texte | base64 en haute résolution |
base64 avec numeric=1 si chiffres uniquement |
5. Éviter le proxy quand il est inutile
Le routage proxy ajoute de la latence : ne l'activez que si le site exige une IP précise.
# Without proxy — faster for most use cases
data = {
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
}
# With proxy — only when required
data["proxy"] = "user:pass@1.2.3.4:8080"
data["proxytype"] = "HTTP"
Chaque saut proxy ajoute un aller-retour réseau ; réservez-le aux sites qui filtrent par IP.
6. Remplacer le polling par un callback (pingback)
Le paramètre pingback supprime la boucle d'interrogation.
resp = session.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1",
"pingback": "https://your-server.com/captcha-callback",
})
CaptchaAI pousse le résultat vers votre URL dès la fin de la résolution : c'est la méthode la plus rapide.
- L'endpoint doit être public — un service derrière OVHcloud ou Scaleway convient.
- Côté RGPD, journalisez le temps de résolution et l'ID de tâche, pas les sessions.
7. Mesurer l'effet de vos optimisations
Mesurez la latence de bout en bout, avant et après chaque changement.
import statistics
def benchmark(solve_func, iterations=20):
times = []
for i in range(iterations):
start = time.time()
try:
solve_func()
times.append(time.time() - start)
except Exception:
pass
if times:
print(f"Samples: {len(times)}/{iterations}")
print(f"Mean: {statistics.mean(times):.1f}s")
print(f"Median: {statistics.median(times):.1f}s")
print(f"P95: {sorted(times)[int(len(times)*0.95)]:.1f}s")
print(f"Min: {min(times):.1f}s")
print(f"Max: {max(times):.1f}s")
Suivez le P95, pas la moyenne : c'est lui qui gouverne la charge.
Dépannage
| Problème | Cause probable | Correctif |
|---|---|---|
| Latence de polling inchangée | requests.get() sans session |
Passez à session.get() |
| Jetons prefetch expirés | Traitement de page trop long | Réduisez la fenêtre d'anticipation |
| Callback jamais reçu | Serveur injoignable depuis CaptchaAI | Exposez une URL publique |
| Rate limiting sur polling rapide | Interrogation < 2 s | Intervalle minimum : 3 s |
FAQ
Le pingback fonctionne-t-il derrière un pare-feu d'entreprise ?
Seulement si CaptchaAI peut atteindre votre URL. Derrière un NAT fermé, gardez le polling adaptatif.
Faut-il plus de threads pour réduire la latence ?
Non : les threads augmentent le débit en parallèle, pas la vitesse d'un solve isolé. Le plan BASIC ($15/mois, 5 threads) suffit pour tester.
Comment mesurer la latence réelle de bout en bout ?
Enveloppez votre fonction dans le benchmark() de la section 7 et comparez médiane et P95 sur 20 itérations.
Le prefetch risque-t-il de faire expirer les jetons ?
Oui, si la fenêtre est trop large. Soumettez la page N+1 une fois le traitement de N engagé.
Passez à des résolutions plus rapides avec CaptchaAI
Récupérez votre clé API sur le tableau de bord CaptchaAI, branchez le polling adaptatif, puis passez au pingback.