Tutorials

Débit de résolution de CAPTCHA : comment traiter 10 000 tâches par heure

Pour traiter 10 000 CAPTCHA par heure, le facteur décisif n'est pas la vitesse d'une résolution isolée : c'est le nombre de résolutions que vous gardez en vol en parallèle. Avec CaptchaAI, une résolution de reCAPTCHA v2 prend une quinzaine de secondes en médiane observée ; en superposant assez de requêtes simultanées, vous atteignez 10 000 résolutions par heure — soit environ 2,8 par seconde en continu. Ce guide couvre le calcul de concurrence, l'architecture, le code Python et Node.js, puis le réglage du débit.

Le calcul de la concurrence

Partez de la médiane observée : une résolution de reCAPTCHA v2 prend environ 15 secondes. Le reste n'est qu'une affaire d'empilement.

  • En séquentiel : 3 600 s / 15 s = 240 résolutions par heure
  • Pour viser 10 000 par heure : il vous faut ~42 résolutions simultanées en vol à tout instant

D'une résolution isolée à un flux continu

L'idée clé : vous n'attendez pas que CaptchaAI aille plus vite. Vous empilez assez de requêtes pour que 42 résolutions se terminent dans la même fenêtre de 15 secondes. Le débit vient de la concurrence, pas de la latence d'une résolution unique.

De la concurrence aux threads facturés

Ces 42 résolutions simultanées se traduisent en threads facturés : CaptchaAI facture au thread concurrent (un thread = une résolution en vol), avec des résolutions illimitées par thread. Pour tenir 42 à 50 résolutions en parallèle, visez le plan ADVANCE ($90/mois, 50 threads) ; STANDARD ($30/mois, 15 threads) plafonne bien avant. Pour la configuration agressive à 100 en parallèle, il faut PREMIUM ($170/mois, 100 threads).

Simultanéité visée Plan CaptchaAI Threads
Jusqu'à 15 en vol STANDARD ($30/mois) 15
42 à 50 en vol ADVANCE ($90/mois) 50
Jusqu'à 100 en vol PREMIUM ($170/mois) 100

L'architecture du pipeline

┌──────────┐     ┌────────────┐     ┌─────────────┐     ┌──────────┐
│  Task     │────▶│  Submit    │────▶│  CaptchaAI  │────▶│  Result  │
│  Queue    │     │  Workers   │     │  API        │     │  Store   │
│  (Redis)  │     │  (async)   │     │             │     │  (DB)    │
└──────────┘     └────────────┘     └─────────────┘     └──────────┘
                       │                    ▲
                       │    ┌──────────┐    │
                       └───▶│  Poll    │────┘
                            │  Workers │
                            └──────────┘
  1. File d'attente des tâches — conserve les CAPTCHA en attente avec leur sitekey et leur URL (Redis convient bien).
  2. Workers de soumission — envoient les tâches à l'API CaptchaAI en parallèle.
  3. Workers d'interrogation — récupèrent les résultats par polling à intervalles réglés.
  4. Stockage des résultats — enregistre chaque token dès son arrivée.

Les deux familles de workers sont découplées : les soumissions n'attendent jamais les résultats, et l'interrogation tourne à son propre rythme. C'est ce découplage qui laisse monter la concurrence sans bloquer le pipeline.

Déploiement et RGPD

Si vous déployez ces workers chez un hébergeur européen (OVHcloud, Scaleway, région eu-west-3 de Paris), gardez le stockage des résultats au plus près des workers pour limiter la latence. Minimisez au passage les données personnelles conservées, afin de rester aligné avec vos obligations RGPD.

Python : le pipeline asynchrone

Le script ci-dessous s'appuie sur asyncio et aiohttp : un sémaphore borne la concurrence, chaque tâche soumet puis interroge son résultat, et un compteur agrégé suit le débit réel.

# high_throughput_solver.py
import os
import asyncio
import time
import aiohttp

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
BASE_URL = "https://ocr.captchaai.com"
MAX_CONCURRENT = 50  # Max simultaneous solves
POLL_INTERVAL = 5    # Seconds between polls
INITIAL_WAIT = 12    # Seconds before first poll

semaphore = asyncio.Semaphore(MAX_CONCURRENT)
stats = {"submitted": 0, "solved": 0, "failed": 0, "start": 0}

async def solve_one(session, sitekey, pageurl, task_num):
    """Submit and poll a single CAPTCHA."""
    async with semaphore:
        try:
            # Submit
            async with session.get(f"{BASE_URL}/in.php", params={
                "key": API_KEY, "method": "userrecaptcha",
                "googlekey": sitekey, "pageurl": pageurl, "json": "1",
            }) as resp:
                result = await resp.json(content_type=None)

            if result.get("status") != 1:
                stats["failed"] += 1
                return None

            stats["submitted"] += 1
            task_id = result["request"]

            # Wait before first poll
            await asyncio.sleep(INITIAL_WAIT)

            # Poll
            for _ in range(25):
                async with session.get(f"{BASE_URL}/res.php", params={
                    "key": API_KEY, "action": "get",
                    "id": task_id, "json": "1",
                }) as resp:
                    poll_result = await resp.json(content_type=None)

                if poll_result.get("status") == 1:
                    stats["solved"] += 1
                    return poll_result["request"]

                if poll_result.get("request") != "CAPCHA_NOT_READY":
                    stats["failed"] += 1
                    return None

                await asyncio.sleep(POLL_INTERVAL)

            stats["failed"] += 1
            return None

        except Exception as e:
            stats["failed"] += 1
            return None

async def run_batch(tasks):
    """Process a batch of CAPTCHA tasks concurrently."""
    connector = aiohttp.TCPConnector(
        limit=MAX_CONCURRENT,
        keepalive_timeout=60,
    )
    async with aiohttp.ClientSession(connector=connector) as session:
        coros = [
            solve_one(session, task["sitekey"], task["pageurl"], i)
            for i, task in enumerate(tasks)
        ]
        results = await asyncio.gather(*coros)
    return results

async def main():
    # Generate test tasks (replace with your task source)
    tasks = [
        {
            "sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
            "pageurl": "https://www.google.com/recaptcha/api2/demo",
        }
        for _ in range(100)  # Start with 100 tasks
    ]

    stats["start"] = time.time()
    print(f"Processing {len(tasks)} tasks with {MAX_CONCURRENT} concurrent workers")

    results = await run_batch(tasks)
    elapsed = time.time() - stats["start"]

    print(f"\nCompleted in {elapsed:.0f}s")
    print(f"Submitted: {stats['submitted']}")
    print(f"Solved: {stats['solved']}")
    print(f"Failed: {stats['failed']}")
    print(f"Throughput: {stats['solved'] / (elapsed / 3600):.0f} solves/hour")

asyncio.run(main())

JavaScript (Node.js) : le pipeline concurrent

Côté Node.js, la même logique repose sur axios et un agent HTTPS keep-alive. La fonction runWithConcurrency plafonne le nombre de résolutions en vol à l'aide d'un Set de promesses et de Promise.race.

// high_throughput_solver.js
const axios = require('axios');
const https = require('https');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const BASE = 'https://ocr.captchaai.com';
const MAX_CONCURRENT = 50;

const agent = new https.Agent({ keepAlive: true, maxSockets: MAX_CONCURRENT });
const api = axios.create({ baseURL: BASE, httpsAgent: agent, timeout: 30000 });

const stats = { submitted: 0, solved: 0, failed: 0 };

async function solveOne(sitekey, pageurl) {
  try {
    const submit = await api.get('/in.php', {
      params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: '1' },
    });
    if (submit.data.status !== 1) { stats.failed++; return null; }
    stats.submitted++;

    await new Promise(r => setTimeout(r, 12000));

    for (let i = 0; i < 25; i++) {
      const poll = await api.get('/res.php', {
        params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
      });
      if (poll.data.status === 1) { stats.solved++; return poll.data.request; }
      if (poll.data.request !== 'CAPCHA_NOT_READY') { stats.failed++; return null; }
      await new Promise(r => setTimeout(r, 5000));
    }
    stats.failed++;
    return null;
  } catch { stats.failed++; return null; }
}

async function runWithConcurrency(tasks, limit) {
  const results = [];
  const executing = new Set();

  for (const task of tasks) {
    const p = solveOne(task.sitekey, task.pageurl).then(r => {
      executing.delete(p);
      return r;
    });
    executing.add(p);
    results.push(p);

    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }
  return Promise.all(results);
}

(async () => {
  const tasks = Array.from({ length: 100 }, () => ({
    sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
    pageurl: 'https://www.google.com/recaptcha/api2/demo',
  }));

  const start = Date.now();
  console.log(`Processing ${tasks.length} tasks, ${MAX_CONCURRENT} concurrent`);

  await runWithConcurrency(tasks, MAX_CONCURRENT);
  const elapsed = (Date.now() - start) / 1000;

  console.log(`\nDone in ${elapsed.toFixed(0)}s`);
  console.log(`Solved: ${stats.solved}, Failed: ${stats.failed}`);
  console.log(`Throughput: ${(stats.solved / (elapsed / 3600)).toFixed(0)} solves/hour`);

  agent.destroy();
})();

Régler la concurrence

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.

Paramètre Conservateur Équilibré Agressif
MAX_CONCURRENT 20 50 100
INITIAL_WAIT 15 s 12 s 10 s
POLL_INTERVAL 7 s 5 s 3 s
MAX_POLL_ATTEMPTS 30 25 20
Débit attendu ~4 800/h ~10 000/h ~18 000/h

Commencez prudemment, puis augmentez MAX_CONCURRENT jusqu'à voir les rendements diminuer ou le taux d'erreur monter.

Dépannage

Problème Cause Correctif
Le débit plafonne vers ~5 000/h Concurrence insuffisante Augmentez MAX_CONCURRENT à 80–100
Taux d'erreur > 10 % API surchargée ou proxys défaillants Réduisez la concurrence, vérifiez la santé des proxys
La mémoire ne cesse de grimper Accumulation illimitée des tâches Traitez les résultats au fil de l'eau, ne les mettez pas en tampon
ERROR_NO_SLOT_AVAILABLE File d'attente CaptchaAI pleine Faites une pause et réessayez après 5 secondes

Surveiller le débit en production

Suivez ces métriques en temps réel pour garder le pipeline sur son objectif :

  • Résolutions par minute — visez ~167 pour tenir l'objectif de 10 000/heure.
  • Taux d'erreur — gardez-le sous 5 %. En cas de pic, réduisez la concurrence.
  • Profondeur de la file d'attente — si elle grossit, ajoutez des workers ; si elle se vide, vous êtes surdimensionné.
  • Temps de résolution P90 — s'il augmente, l'API limite peut-être le débit.

FAQ

Quel plan et combien de threads pour 10 000 résolutions par heure ?

Il vous faut de quoi maintenir ~42 résolutions simultanées, soit au moins 50 threads : le plan ADVANCE ($90/mois, 50 threads). STANDARD ($30/mois, 15 threads) plafonne trop tôt ; pour une marge agressive, PREMIUM ($170/mois, 100 threads) tient 100 résolutions en parallèle. La facturation se fait au thread, avec des résolutions illimitées par thread.

Faut-il des proxys pour atteindre ce débit ?

Non, pas côté CaptchaAI : le service prend en charge la résolution. Les proxys concernent votre propre trafic vers les sites cibles. Si vous en utilisez, surveillez leur santé : un pool saturé fait grimper le taux d'erreur bien avant l'API.

Peut-on répartir le pipeline sur plusieurs machines ?

Oui. Placez la file d'attente dans un service partagé (Redis, RabbitMQ) et lancez le script de worker sur plusieurs serveurs. Chaque worker tire ses tâches indépendamment, ce qui répartit naturellement la charge.

Pourquoi le débit plafonne-t-il malgré une concurrence élevée ?

Le plus souvent, la concurrence réelle reste sous la valeur configurée à cause d'un pool de connexions trop petit ou de proxys lents. Vérifiez le keepAlive, surveillez le P90, et gardez un œil sur votre solde via res.php?action=getbalance : un solde épuisé interrompt les soumissions sans prévenir.

Prochaines étapes

Lancez votre pipeline CAPTCHA à haut débit — récupérez votre clé API CaptchaAI. Pour aller plus loin :

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