API-Tutorials

CaptchaAI API-Latenzoptimierung: Schnellere Lösungen

Wenn ein Scraping- oder Test-Job doppelt so lange läuft wie geplant, liegt das selten am Solver. Die zusätzlichen Sekunden entstehen fast immer im eigenen Code: ein starres Polling-Intervall von 5 Sekunden, eine neue TCP-Verbindung pro Anfrage und ein Ablauf, der das CAPTCHA erst nach dem Seitenaufruf einreicht.

Die Solver-Laufzeit selbst können Sie nicht beeinflussen – alles davor und danach schon, und zwar ohne Architekturumbau.

Wo die Zeit bei einer CAPTCHA-Lösung vergeht

Zerlegen Sie eine Lösung in vier Abschnitte, bevor Sie optimieren:

  • Übermittlung – die Anfrage an in.php, inklusive TCP- und TLS-Handshake.
  • Warteschlange – die Zeit, bis ein freier Thread die Aufgabe übernimmt.
  • Solver-Laufzeit – begrenzt durch die Obergrenzen pro CAPTCHA-Typ.
  • Abholung – das Polling gegen res.php, bis das Token bereitliegt.

Drei der vier Abschnitte liegen in Ihrer Hand.

1. Polling-Intervalle adaptiv statt starr setzen

Ein festes Intervall von 5 Sekunden kostet im Mittel die halbe Intervalllänge: Ist die Lösung eine Sekunde nach der letzten Abfrage fertig, wartet Ihr Code vier Sekunden weiter. Adaptives Polling fragt anfangs eng ab und geht danach zurück.

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`);
}

Gegenüber einem festen 5-Sekunden-Intervall spart das im Schnitt 1–4 Sekunden pro Lösung. Drei Regeln dazu:

  • Nicht unter 3 Sekunden abfragen – die Lösung wird dadurch nicht früher fertig, das Risiko von Rate-Limiting steigt aber.
  • Nach vier engen Abfragen auf 5 Sekunden zurückgehen.
  • Ein Timeout von 120 Sekunden setzen, damit ein hängender Task keinen Worker blockiert.

2. HTTP-Verbindungen wiederverwenden

Jede neue Verbindung kostet einen TCP- und einen TLS-Handshake. Bei sechs bis acht Abfragen pro Lösung summiert sich das – spürbar, sobald ein Job zehntausende Seiten verarbeitet:

Python

session = requests.Session()
# Use session.get() and session.post() instead of requests.get/post
# The session reuses TCP connections automatically

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

Das spart rund 50–100 ms pro Anfrage – am deutlichsten bei Bild- und Grid-CAPTCHAs, wo der Handshake einen großen Anteil an der Gesamtdauer hat.

3. CAPTCHAs vorab einreichen, statt zu warten

Der größte Hebel ist nicht schnelleres, sondern paralleles Warten. Während Ihr Crawler Seite N verarbeitet, reichen Sie die Aufgabe für Seite N+1 bereits ein:

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)

So überlappt die Lösungszeit mit der Verarbeitung, und die spürbare Wartezeit sinkt gegen null. Zwei Grenzen:

  • Tokens sind nicht beliebig lange gültig – bei reCAPTCHA liegt das Fenster bei rund 120 Sekunden. Reichen Sie nur so weit im Voraus ein, wie Ihre Verarbeitung dauert.
  • Jede offene Aufgabe belegt einen Thread. Zehn Seiten im Voraus brauchen zehn freie Threads.

Praxisbeispiel: Preismonitoring auf einem Hetzner-Server

Ein Preismonitoring-Job läuft nachts auf einem Cloud-Server in Falkenstein und ruft 400 Produktseiten ab, 120 davon mit reCAPTCHA-v2-Abfrage. Sequenziell abgearbeitet steht der Job bei jeder dieser Seiten still, bis das Token vorliegt – die Wartezeit addiert sich vollständig auf die Laufzeit.

Wird die nächste Seite vorab eingereicht, verschwindet der Großteil davon im Hintergrund. Läuft derselbe Job als GitLab-CI-Schedule, zählt das doppelt: Eine kürzere Laufzeit passt in ein enges Wartungsfenster und kostet weniger Runner-Minuten.

4. Pro Szenario die schnellere Methode wählen

Zwei Fälle, in denen ein anderer Weg messbar schneller ist:

Szenario Langsamer Weg Schnellere Alternative
reCAPTCHA v2 mit bekannter Callback-Funktion userrecaptcha + Polling userrecaptcha mit pingback (Callback-URL)
Bild-CAPTCHA mit reinem Zifferninhalt base64 in voller Auflösung base64 mit numeric=1

5. Proxy nur senden, wenn die Zielseite ihn verlangt

Jeder Proxy fügt einen Netzwerk-Hop hinzu. Übergeben Sie die Parameter nur, wenn die Zielseite die Anfrage aus einem bestimmten Netz erwartet:

# 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:[email protected]:8080"
data["proxytype"] = "HTTP"

Zwei Hinweise:

  • Ein Residential-Proxy ist langsamer als ein Rechenzentrums-Proxy und kostet ohne Not nur Zeit.
  • IP-Adressen gelten nach DSGVO als personenbezogene Daten. Prüfen Sie Rechtsgrundlage und Auftragsverarbeitung, bevor Sie Traffic über einen fremden Anbieter leiten.

6. Polling vermeiden: Callback-URLs mit pingback

Hat Ihre Anwendung einen öffentlich erreichbaren Endpunkt, entfällt die Abholphase ganz:

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 sendet das Ergebnis an die angegebene URL, sobald die Lösung vorliegt. Voraussetzung:

  • öffentliche HTTPS-URL, kein Dienst hinter NAT oder VPN,
  • Firewall-Regeln, die eingehende Anfragen zulassen,
  • eine Zuordnung der Task-ID zum wartenden Prozess, etwa über Redis.

Threads bestimmen den Durchsatz, Polling die Einzellatenz

Zwei Größen werden oft verwechselt: Polling, Verbindungs-Pooling und Prefetching senken die Latenz einer Lösung. Wie viele Lösungen gleichzeitig laufen, hängt an den Threads – CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung, mit unbegrenzt vielen Lösungen pro Thread:

  • BASIC – 15 $ pro Monat, 5 Threads
  • ADVANCE – 90 $ pro Monat, 50 Threads
  • ENTERPRISE – 300 $ pro Monat, 200 Threads

Wer 40 Seiten parallel verarbeitet, braucht entsprechend viele Threads; ein engeres Intervall ersetzt sie nicht. Preise in US-Dollar.

7. Vor und nach jeder Änderung messen

Ohne Messreihe bleibt Tuning Bauchgefühl. Zwanzig Durchläufe pro Konfiguration reichen für eine Tendenz; aussagekräftig sind Median und P95:

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")

Notieren Sie zu jedem Lauf Datum, CAPTCHA-Typ und Thread-Zahl – ohne diesen Kontext sind zwei Messreihen nicht vergleichbar.

Welche Zielwerte pro CAPTCHA-Typ realistisch sind

Die Obergrenzen pro Typ stecken den Rahmen ab. Je kürzer die Lösung selbst dauert, desto stärker fallen Handshake und Polling-Overhead ins Gewicht:

CAPTCHA-Typ Obergrenze Größter Hebel
Bild/OCR < 0,5 s Verbindungs-Pooling
reCAPTCHA v3 < 4 s Polling-Intervall
Cloudflare Turnstile < 10 s Polling-Intervall
GeeTest v3 < 12 s Polling-Intervall
reCAPTCHA v2 < 60 s Prefetching, Callback-URL

Typische Stolpersteine

Symptom Ursache Lösung
Latenz sinkt trotz adaptivem Polling nicht Aufrufe laufen weiter über requests.get() Auf session.get() umstellen
Vorab eingereichte Tokens sind abgelaufen Verarbeitung dauert länger als die Gültigkeit Prefetch-Fenster verkleinern
Callback-URL erhält nie Daten Endpunkt von außen nicht erreichbar URL und Firewall-Regeln prüfen
Enges Polling erzeugt Fehlerantworten Abfragen unter 2 s lösen Rate-Limiting aus Mindestintervall 3 Sekunden
Aufgaben warten in der Warteschlange Alle Threads belegt Parallelität begrenzen oder Threads aufstocken

Häufige Fragen

Wie kurz darf das Polling-Intervall sein?

Drei Sekunden sind die Untergrenze. Darunter steigt nur die Zahl der CAPCHA_NOT_READY-Antworten, und Sie riskieren Rate-Limiting.

Brauche ich mehr Threads oder ein besseres Polling?

Es kommt darauf an, wo Sie warten. Polling und Prefetching senken die Latenz einer Lösung, Threads erhöhen den Durchsatz. Stehen Aufgaben in der Warteschlange, hilft nur mehr Parallelität – etwa ADVANCE mit 50 Threads für 90 $ pro Monat.

Was passiert, wenn ein vorab eingereichtes Token abläuft?

Die Zielseite lehnt es ab, und Sie brauchen eine neue Lösung. reCAPTCHA-Tokens sind rund 120 Sekunden gültig – halten Sie das Prefetch-Fenster deutlich darunter.

Lohnt sich pingback in einer CI-Pipeline?

Nur, wenn der Runner einen öffentlich erreichbaren Endpunkt hat. In vielen GitLab-CI-Setups läuft er hinter NAT; dann bleibt adaptives Polling die praktikablere Variante.

Warum schwanken die Messwerte zwischen zwei Läufen?

Weil Warteschlange, Netzwerkweg und CAPTCHA-Konfiguration der Zielseite variieren. Vergleichen Sie Median und P95 aus mindestens 20 Läufen, nie Einzelmessungen.

Latenz senken – mit CaptchaAI

Holen Sie sich Ihren API-Schlüssel unter captchaai.com und stellen Sie Ihre Pipeline auf adaptives Polling, wiederverwendete Verbindungen und vorab eingereichte Aufgaben um.

Weiterlesen

Kommentare sind für diesen Artikel deaktiviert.