Referenz

CaptchaAI Ratenbegrenzungen und Drosselung

CaptchaAI zählt keine Anfragen pro Sekunde mit: Es gibt weder ein hartes Submit-Limit noch ein Tageskontingent. Ihren Durchsatz begrenzen genau zwei Größen – die Threads Ihres Plans und die momentane Auslastung der Worker. Wer beide kennt, drosselt an der richtigen Stelle und sieht ERROR_NO_SLOT_AVAILABLE deutlich seltener.

Dieser Leitfaden zeigt, welche Grenzen tatsächlich gelten und mit welchen Bausteinen – Backoff, Token-Bucket, Semaphore, adaptives Polling – Sie Ihre Pipeline auch bei Lastspitzen ruhig halten.

Rate-Limits bei CaptchaAI: welche Grenzen wirklich gelten

CaptchaAI arbeitet nicht mit klassischem Rate-Limiting pro Sekunde. Die Kapazität ergibt sich aus der Verfügbarkeit der Worker und aus Ihrer Thread-Zuteilung:

Faktor Verhalten
Übermittlungsrate kein festes Limit pro Sekunde
Gleichzeitige Tasks so viele, wie Ihr Plan an Threads vorsieht
Polling-Intervall empfohlen: alle 5 Sekunden pro Task
Guthabenabfrage kein Limit

Ein Thread ist genau ein CAPTCHA, das gerade in Bearbeitung ist. Sobald die Lösung zurückkommt, nimmt derselbe Thread die nächste Aufgabe an. Abgerechnet wird pro Thread und Monat – nicht pro Lösung, ohne Aufschlag nach CAPTCHA-Typ und ohne tägliche Obergrenze. BASIC beginnt bei 15 $ mit 5 Threads, ENTERPRISE liegt bei 300 $ mit 200 Threads; die vollständige Staffel bis VIP-3 steht auf der Preisseite. (Preise in US-Dollar.)

Sind alle Worker belegt, beantwortet in.php neue Übermittlungen mit ERROR_NO_SLOT_AVAILABLE. Das ist weder ein Fehler in Ihrem Code noch eine Sperre Ihres Kontos, sondern ein Rückstausignal: Die Warteschlange ist voll, versuchen Sie es kurz darauf erneut.

Kapazität planen: ab welchem Volumen Drosselung nötig wird

Bevor Sie Code schreiben, sollten Sie Ihre Größenordnung kennen:

Volumen Parallelität Strategie
unter 100/Stunde 1–5 sequenziell, keine Drosselung nötig
100–1.000/Stunde 5–20 Parallelität über eine Semaphore begrenzen
1.000–10.000/Stunde 20–50 asynchron mit Warteschlange und Callbacks
über 10.000/Stunde 50–100 Worker-Pool, dedizierte Kapazität

Ab etwa 10.000 Lösungen pro Stunde lohnt sich eine Rücksprache mit dem CaptchaAI-Support über dedizierte Kapazität.

Ein Beispiel aus der Praxis: Ein Preisvergleichsdienst betreibt seine Crawler auf zwei Hetzner-VPS und startet die Läufe nachts per GitLab-CI-Schedule. Tagsüber fallen kaum 200 CAPTCHAs pro Stunde an, zwischen 2 und 4 Uhr starten alle Jobs gleichzeitig und erzeugen das Zehnfache. Ohne clientseitige Begrenzung feuert die Pipeline alles auf einmal ab; eine Semaphore auf 40 gleichzeitige Tasks verteilt dieselbe Last gleichmäßig und senkt die Fehlerquote spürbar. Ähnlich verhalten sich Terminportale wie die BLS-Buchungsstrecken: wenige Minuten hohe Last, danach lange Ruhe.

Hinweis zur DSGVO: Laufen Ihre Crawler über Residential-Proxys, gehört die Datenschutzfrage in dieselbe Planung. IP-Adressen sind personenbezogene Daten; Rechtsgrundlage und Datenflüsse sollten Sie unabhängig von der technischen Drosselung dokumentieren.

ERROR_NO_SLOT_AVAILABLE mit exponentiellem Backoff abfangen

Faustregel: Bei einem belegten Slot warten und erneut übermitteln – mit wachsendem Abstand und einer Obergrenze, damit ein einzelner Task nicht endlos hängt.

Python

import time
import requests

API_KEY = "YOUR_API_KEY"


def submit_with_backoff(params, max_retries=5):
    params["key"] = API_KEY

    for attempt in range(max_retries):
        resp = requests.get(
            "https://ocr.captchaai.com/in.php", params=params
        )

        if resp.text.startswith("OK|"):
            return resp.text.split("|")[1]

        if resp.text == "ERROR_NO_SLOT_AVAILABLE":
            wait = min(2 ** attempt * 2, 60)  # 2, 4, 8, 16, 32s max 60
            print(f"No slots, waiting {wait}s (attempt {attempt + 1})")
            time.sleep(wait)
            continue

        raise Exception(f"Submit error: {resp.text}")

    raise Exception("Max retries exceeded — no slots available")

Node.js

async function submitWithBackoff(params, maxRetries = 5) {
  params.key = process.env.CAPTCHAAI_API_KEY;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const resp = await axios.get("https://ocr.captchaai.com/in.php", {
      params,
    });
    const text = String(resp.data);

    if (text.startsWith("OK|")) {
      return text.split("|")[1];
    }

    if (text === "ERROR_NO_SLOT_AVAILABLE") {
      const wait = Math.min(2 ** attempt * 2000, 60000);
      console.log(`No slots, waiting ${wait}ms (attempt ${attempt + 1})`);
      await new Promise((r) => setTimeout(r, wait));
      continue;
    }

    throw new Error(`Submit error: ${text}`);
  }

  throw new Error("Max retries exceeded");
}

Beide Varianten verdoppeln die Wartezeit pro Versuch und deckeln sie bei 60 Sekunden. Entscheidend ist die Unterscheidung im Code: Nur ERROR_NO_SLOT_AVAILABLE wird wiederholt. Alle anderen Rückmeldungen – etwa ein ungültiger Schlüssel oder ein fehlender sitekey – sind dauerhafte Fehler, die ein erneuter Versuch nicht heilt; sie gehören sofort ins Logging.

Clientseitig drosseln: Token-Bucket und Semaphore

Backoff reagiert erst, wenn es zu spät ist. Ruhiger läuft eine Pipeline, die von vornherein nicht mehr Last erzeugt, als sie verarbeiten kann. Zwei Bausteine genügen dafür: Ein Token-Bucket begrenzt die Übermittlungen pro Sekunde, eine Semaphore die Zahl der offenen Tasks.

Token-Bucket in Python

import time
import threading


class RateLimiter:
    def __init__(self, rate, per=1.0):
        """Allow `rate` requests per `per` seconds."""
        self.rate = rate
        self.per = per
        self.tokens = rate
        self.last_refill = time.monotonic()
        self.lock = threading.Lock()

    def acquire(self):
        with self.lock:
            now = time.monotonic()
            elapsed = now - self.last_refill
            self.tokens = min(self.rate, self.tokens + elapsed * (self.rate / self.per))
            self.last_refill = now

            if self.tokens >= 1:
                self.tokens -= 1
                return
            else:
                sleep_time = (1 - self.tokens) * (self.per / self.rate)

        time.sleep(sleep_time)
        self.acquire()


# Allow 10 submissions per second
limiter = RateLimiter(rate=10, per=1.0)

def submit_limited(params):
    limiter.acquire()
    return submit_with_backoff(params)

Parallelität mit einer Asyncio-Semaphore begrenzen

import asyncio

# Limit to 20 concurrent tasks
semaphore = asyncio.Semaphore(20)

async def solve_limited(solver, session, params):
    async with semaphore:
        return await solver.solve(session, params)

Setzen Sie die Semaphore auf die Thread-Zahl Ihres Plans oder knapp darunter. Höher zu gehen bringt nichts: Die zusätzlichen Tasks warten dann auf der Serverseite statt in Ihrem Prozess.

Polling-Intervalle richtig wählen

res.php sollte pro Task nicht häufiger als alle 5 Sekunden abgefragt werden. Kürzere Abstände beschleunigen nichts, erzeugen aber unnötige Anfragen. Bewährt hat sich ein adaptives Muster: kurze Intervalle am Anfang, längere, je länger die Lösung dauert.

async def smart_poll(session, task_id, solver):
    """Polls with adaptive intervals."""
    intervals = [5, 5, 5, 10, 10, 15, 15, 30, 30, 60]

    for wait in intervals:
        await asyncio.sleep(wait)
        result = await solver.check(session, task_id)
        if result is not None:
            return result

    raise TimeoutError(f"Task {task_id} timed out")

Die Staffel deckt rund 3 Minuten ab. Bei schnellen Typen wie Cloudflare Turnstile liefert meist schon die erste oder zweite Abfrage ein Ergebnis; ein Rasterbild-CAPTCHA erreicht häufiger die späteren Intervalle.

Nutzung messen: Anfragerate und Fehlerquote

Ohne Messwerte ist jede Drosselung geraten. Ein schlankes Zeitfenster über die letzten 60 Sekunden genügt, um Anfragerate und Fehlerquote laufend zu sehen:

import time
from collections import deque


class APIMetrics:
    def __init__(self, window=60):
        self.window = window
        self.requests = deque()
        self.errors = deque()

    def record_request(self):
        now = time.time()
        self.requests.append(now)
        self._cleanup(self.requests, now)

    def record_error(self, error_code):
        now = time.time()
        self.errors.append((now, error_code))
        self._cleanup_tuples(self.errors, now)

    def get_rate(self):
        now = time.time()
        self._cleanup(self.requests, now)
        return len(self.requests) / self.window

    def get_error_rate(self):
        now = time.time()
        self._cleanup(self.requests, now)
        self._cleanup_tuples(self.errors, now)
        if not self.requests:
            return 0
        return len(self.errors) / len(self.requests)

    def _cleanup(self, dq, now):
        while dq and dq[0] < now - self.window:
            dq.popleft()

    def _cleanup_tuples(self, dq, now):
        while dq and dq[0][0] < now - self.window:
            dq.popleft()


metrics = APIMetrics()

# Use in your submit function
def submit_tracked(params):
    metrics.record_request()
    try:
        return submit_with_backoff(params)
    except Exception as e:
        metrics.record_error(str(e))
        raise

# Check metrics periodically
print(f"Rate: {metrics.get_rate():.1f} req/s")
print(f"Error rate: {metrics.get_error_rate():.1%}")

Zwei Werte verdienen eine Alarmschwelle. Steigt die Fehlerquote über wenige Prozent, ist Ihre Parallelität zu hoch angesetzt. Bleibt die Anfragerate dauerhaft deutlich unter dem, was Ihr Plan an Threads hergibt, drosseln Sie zu stark und verschenken Durchsatz.

Drei Drosselungsfehler, die in Produktion teuer werden

  • Sofortiger erneuter Versuch ohne Wartezeit. Eine Schleife, die nach ERROR_NO_SLOT_AVAILABLE unmittelbar wieder übermittelt, verschärft den Rückstau, statt ihn abzubauen.
  • Backoff ohne Obergrenze. Ohne Deckel wächst die Wartezeit ins Absurde; 60 Sekunden sind ein praxistauglicher Maximalwert.
  • Drosselung nur pro Prozess. Laufen fünf Container parallel, gilt Ihr Limit fünfmal. Verwalten Sie das Kontingent zentral, etwa über einen gemeinsamen Zähler in Redis.

Häufige Fragen

Wie viele CAPTCHAs kann ich gleichzeitig lösen lassen?

So viele, wie Ihr Plan an Threads enthält: 5 bei BASIC, 200 bei ENTERPRISE, 5.000 bei VIP-3. Innerhalb dieser Grenze ist die Zahl der Lösungen pro Monat nicht gedeckelt.

Wie lange sollte ein Backoff maximal warten?

Etwa 60 Sekunden. Danach ist es sinnvoller, den Task in eine Warteschlange zurückzustellen, als den Prozess weiter zu blockieren.

Kostet ein zurückgewiesener Submit zusätzliches Guthaben?

Nein. Abgerechnet wird pro Thread und Monat, nicht pro Lösung – ein zurückgewiesener Submit erzeugt keine separate Gebühr und belegt nur kurz eine Verbindung.

Warum bleibt ERROR_NO_SLOT_AVAILABLE dauerhaft stehen?

Dann liegt Ihre Dauerlast über der Thread-Zuteilung. Prüfen Sie zuerst, ob die Semaphore wirklich greift; reicht das nicht, erhöht ein größerer Plan die Thread-Zahl.

Woran erkenne ich, dass meine Drosselung zu streng eingestellt ist?

An einer niedrigen Anfragerate bei gleichzeitig sehr niedriger Fehlerquote. Erhöhen Sie die Parallelität dann schrittweise, bis die Fehlerquote messbar anzieht, und nehmen Sie einen Schritt zurück.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.