Sie können einen neuen CAPTCHA-Anbieter vollständig bewerten, bevor Sie Ihre Produktion dauerhaft umstellen: Lassen Sie CaptchaAI und Ihren bisherigen Dienst dieselben CAPTCHA-Abfragen gleichzeitig lösen und vergleichen Sie Erfolgsquote, Geschwindigkeit und Kosten anhand Ihres echten Traffics. Genau das leistet ein Parallellauf – ein kontrollierter Test, der aus einer Migration eine Entscheidung mit Zahlen statt einer Vermutung macht.
Dieser Leitfaden zeigt den kompletten Aufbau: eine austauschbare Anbieter-Schnittstelle, einen Runner, der beide Dienste gleichzeitig misst, einen schrittweisen Traffic-Split für die Produktion und die Kennzahlen, auf die es beim Umstieg wirklich ankommt. Die Beispiele liegen in Python und JavaScript vor.
Warum Marketing-Benchmarks Ihre Migration nicht absichern
Die Zahlen auf einer Anbieter-Website beschreiben deren Traffic, nicht Ihren. Ihre Erfolgsquote hängt von Faktoren ab, die kein Fremd-Benchmark kennt: Ihre Sitekeys, Ihre Proxy-Konfiguration, die geografische Verteilung Ihrer Anfragen und die Lastspitzen im Tagesverlauf. Erst ein Test mit Ihrer tatsächlichen Arbeitslast zeigt die realen Unterschiede.
Ein Hinweis für DACH-Teams: Führen Sie den Testlauf von einem Server in der Zielregion aus – etwa einer Hetzner- oder netcup-Instanz in Deutschland – und nicht vom lokalen Rechner, damit die Latenz Ihrer Produktion entspricht. Beim Testen mit Residential-Proxys gilt zudem die DSGVO: IP-Adressen sind personenbezogene Daten.
Der Testplan in vier Phasen
Migrieren Sie nicht in einem Schritt, sondern erhöhen Sie den Anteil von CaptchaAI stufenweise – von der reinen Kompatibilitätsprüfung bis zur vollständigen Umstellung:
- Validierung – nur parallel, kein Live-Traffic; prüfen, ob die API-Antworten kompatibel sind.
- Schattentest – ein kleiner Anteil auf CaptchaAI mit Fallback, um Basismetriken zu sammeln.
- Hochfahren – den Anteil stufenweise erhöhen und auf jeder Stufe überwachen.
- Vollständige Umstellung – 100 % auf CaptchaAI, alten Anbieter außer Betrieb nehmen.
| Phase | Dauer | Verkehrsaufteilung | Ziel |
|---|---|---|---|
| 1. Validierung | 1 Tag | 0 % live, nur parallel | API-Kompatibilität überprüfen |
| 2. Schattentest | 3 Tage | 5 % auf CaptchaAI (mit Fallback) | Basismetriken sammeln |
| 3. Hochfahren | 1 Woche | 25 % → 50 % → 75 % | Auf jeder Stufe überwachen |
| 4. Vollständige Umstellung | – | 100 % CaptchaAI | Alten Anbieter außer Betrieb nehmen |
Welche Metriken Sie vergleichen sollten
Die Erfolgsquote allein entscheidet nichts – erst zusammen mit Lösungszeit, Fehlerverteilung und Kosten pro Lösung ergibt sich ein belastbares Bild. Diese Kennzahlen erhebt der Runner in den folgenden Abschnitten automatisch.
| Metrik | So messen Sie |
|---|---|
| Erfolgsquote | successful_solves / total_attempts × 100 |
| Durchschnittliche Lösungszeit | Zeit von der Übermittlung bis zum Erhalt der Lösung |
| P95-Lösungszeit | 95. Perzentil der Lösungszeiten |
| Fehlerquote nach Typ | Jeden Fehlercode separat zählen |
| Kosten pro Lösung | Gesamtausgaben / erfolgreiche Lösungen |
| Token-Gültigkeit | Funktioniert das zurückgegebene Token tatsächlich auf der Zielseite? |
Architektur des Testaufbaus
┌──────────────┐
│ Your App │
└──────┬───────┘
│
┌──────▼───────┐
│ CAPTCHA │
│ Router │
└──┬───────┬───┘
│ │
┌────────▼──┐ ┌──▼────────┐
│ Current │ │ CaptchaAI │
│ Provider │ │ │
└────────┬──┘ └──┬────────┘
│ │
┌──▼───────▼──┐
│ Metrics │
│ Collector │
└─────────────┘
Python-Implementierung
Anbieter als austauschbare Schnittstelle
Kapseln Sie beide Dienste hinter einer einzigen Klasse. Da CaptchaAI die vertrauten in.php- und res.php-Endpunkte nutzt, unterscheiden sich die Anbieter nur in Basis-URL und API-Schlüssel.
import os
import time
import requests
from dataclasses import dataclass, field
from typing import Optional
from concurrent.futures import ThreadPoolExecutor
@dataclass
class SolveResult:
provider: str
success: bool
solution: Optional[str] = None
error: Optional[str] = None
elapsed: float = 0.0
cost: float = 0.0
class CaptchaProvider:
def __init__(self, name, submit_url, result_url, api_key):
self.name = name
self.submit_url = submit_url
self.result_url = result_url
self.api_key = api_key
self.session = requests.Session()
def solve_recaptcha(self, sitekey, pageurl):
start = time.time()
resp = self.session.post(self.submit_url, data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
return SolveResult(
provider=self.name, success=False,
error=data.get("request"), elapsed=time.time() - start
)
captcha_id = data["request"]
for _ in range(60):
time.sleep(5)
result = self.session.get(self.result_url, params={
"key": self.api_key, "action": "get",
"id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
return SolveResult(
provider=self.name, success=True,
solution=result["request"], elapsed=time.time() - start
)
if result.get("request") != "CAPCHA_NOT_READY":
return SolveResult(
provider=self.name, success=False,
error=result.get("request"), elapsed=time.time() - start
)
return SolveResult(
provider=self.name, success=False,
error="TIMEOUT", elapsed=time.time() - start
)
Beide Anbieter parallel ausführen
Der Runner schickt jede Abfrage gleichzeitig an beide Dienste und erzeugt aus Erfolg, Lösungszeit und Fehlercode einen Vergleichsbericht.
class ParallelTestRunner:
def __init__(self, primary, challenger):
self.primary = primary
self.challenger = challenger
self.results = {"primary": [], "challenger": []}
def run_test(self, sitekey, pageurl, num_runs=20):
print(f"Running {num_runs} parallel solves...")
for i in range(num_runs):
with ThreadPoolExecutor(max_workers=2) as executor:
primary_future = executor.submit(
self.primary.solve_recaptcha, sitekey, pageurl
)
challenger_future = executor.submit(
self.challenger.solve_recaptcha, sitekey, pageurl
)
primary_result = primary_future.result()
challenger_result = challenger_future.result()
self.results["primary"].append(primary_result)
self.results["challenger"].append(challenger_result)
print(f" Run {i+1}/{num_runs}: "
f"{self.primary.name}={'OK' if primary_result.success else 'FAIL'} "
f"({primary_result.elapsed:.1f}s) | "
f"{self.challenger.name}={'OK' if challenger_result.success else 'FAIL'} "
f"({challenger_result.elapsed:.1f}s)")
return self.generate_report()
def generate_report(self):
report = {}
for label, results in self.results.items():
total = len(results)
successes = sum(1 for r in results if r.success)
times = [r.elapsed for r in results if r.success]
errors = [r.error for r in results if not r.success]
report[label] = {
"provider": results[0].provider if results else "unknown",
"total": total,
"successes": successes,
"success_rate": (successes / total * 100) if total else 0,
"avg_time": sum(times) / len(times) if times else 0,
"min_time": min(times) if times else 0,
"max_time": max(times) if times else 0,
"errors": errors
}
return report
# Usage
current = CaptchaProvider(
name="CurrentProvider",
submit_url="https://current-provider.com/in.php",
result_url="https://current-provider.com/res.php",
api_key="current_key"
)
captchaai = CaptchaProvider(
name="CaptchaAI",
submit_url="https://ocr.captchaai.com/in.php",
result_url="https://ocr.captchaai.com/res.php",
api_key=os.environ["CAPTCHAAI_API_KEY"]
)
runner = ParallelTestRunner(primary=current, challenger=captchaai)
report = runner.run_test(
sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
pageurl="https://example.com/form",
num_runs=20
)
for label, stats in report.items():
print(f"\n{stats['provider']}:")
print(f" Success rate: {stats['success_rate']:.1f}%")
print(f" Avg time: {stats['avg_time']:.1f}s")
print(f" Min/Max: {stats['min_time']:.1f}s / {stats['max_time']:.1f}s")
if stats['errors']:
print(f" Errors: {stats['errors']}")
Datenverkehr schrittweise umleiten
Sobald die Messung überzeugt, leiten Sie in der Produktion zunächst nur einen kleinen Prozentsatz des Verkehrs an CaptchaAI – mit automatischem Fallback auf den bisherigen Anbieter bei einem Fehlschlag.
import random
class TrafficSplitter:
def __init__(self, primary, challenger, challenger_pct=10):
self.primary = primary
self.challenger = challenger
self.challenger_pct = challenger_pct
def solve(self, sitekey, pageurl):
if random.randint(1, 100) <= self.challenger_pct:
result = self.challenger.solve_recaptcha(sitekey, pageurl)
if not result.success:
# Fall back to primary on failure
return self.primary.solve_recaptcha(sitekey, pageurl)
return result
return self.primary.solve_recaptcha(sitekey, pageurl)
# Start with 10%, increase as confidence builds
splitter = TrafficSplitter(current, captchaai, challenger_pct=10)
result = splitter.solve(sitekey="...", pageurl="...")
JavaScript-Implementierung
Wenn Ihr Stack auf Node.js läuft, bildet dieselbe Logik den Parallellauf ab: eine CaptchaProvider-Klasse pro Dienst und ein Promise.all, das beide Anbieter gleichzeitig aufruft und die Ergebnisse gegenüberstellt.
const axios = require("axios");
class CaptchaProvider {
constructor(name, submitUrl, resultUrl, apiKey) {
this.name = name;
this.submitUrl = submitUrl;
this.resultUrl = resultUrl;
this.apiKey = apiKey;
}
async solveRecaptcha(sitekey, pageurl) {
const start = Date.now();
try {
const submit = await axios.post(this.submitUrl, null, {
params: { key: this.apiKey, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
});
if (submit.data.status !== 1) {
return { provider: this.name, success: false, error: submit.data.request, elapsed: (Date.now() - start) / 1000 };
}
const captchaId = submit.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const poll = await axios.get(this.resultUrl, {
params: { key: this.apiKey, action: "get", id: captchaId, json: 1 },
});
if (poll.data.status === 1) {
return { provider: this.name, success: true, solution: poll.data.request, elapsed: (Date.now() - start) / 1000 };
}
if (poll.data.request !== "CAPCHA_NOT_READY") {
return { provider: this.name, success: false, error: poll.data.request, elapsed: (Date.now() - start) / 1000 };
}
}
return { provider: this.name, success: false, error: "TIMEOUT", elapsed: (Date.now() - start) / 1000 };
} catch (err) {
return { provider: this.name, success: false, error: err.message, elapsed: (Date.now() - start) / 1000 };
}
}
}
async function parallelTest(current, captchaai, sitekey, pageurl, runs = 20) {
const results = { current: [], captchaai: [] };
for (let i = 0; i < runs; i++) {
const [currentResult, captchaaiResult] = await Promise.all([
current.solveRecaptcha(sitekey, pageurl),
captchaai.solveRecaptcha(sitekey, pageurl),
]);
results.current.push(currentResult);
results.captchaai.push(captchaaiResult);
console.log(`Run ${i + 1}/${runs}: ${current.name}=${currentResult.success ? "OK" : "FAIL"} ` +
`(${currentResult.elapsed.toFixed(1)}s) | ${captchaai.name}=${captchaaiResult.success ? "OK" : "FAIL"} ` +
`(${captchaaiResult.elapsed.toFixed(1)}s)`);
}
for (const [label, data] of Object.entries(results)) {
const successes = data.filter((r) => r.success).length;
const times = data.filter((r) => r.success).map((r) => r.elapsed);
const avgTime = times.length ? times.reduce((a, b) => a + b, 0) / times.length : 0;
console.log(`\n${label}: ${successes}/${runs} success (${((successes / runs) * 100).toFixed(1)}%), avg ${avgTime.toFixed(1)}s`);
}
}
// Run
const currentProvider = new CaptchaProvider("CurrentProvider", "https://current-provider.com/in.php", "https://current-provider.com/res.php", "current_key");
const captchaai = new CaptchaProvider("CaptchaAI", "https://ocr.captchaai.com/in.php", "https://ocr.captchaai.com/res.php", process.env.CAPTCHAAI_API_KEY);
parallelTest(currentProvider, captchaai, "SITE_KEY", "https://example.com", 20);
Typische Probleme und Lösungen
| Problem | Ursache | Lösung |
|---|---|---|
| Token wird erzeugt, aber vom Ziel abgelehnt | Sitekey, pageurl oder Session-Kontext stimmen nicht überein | Erfassen Sie die Parameter erneut und verwenden Sie das Token in derselben Browser- oder HTTP-Sitzung |
| Polling endet im Timeout | Intervall, Wartezeit oder Fehlerbehandlung sind zu eng gesetzt | Fragen Sie den Status alle 5–10 Sekunden ab, trennen Sie das Timeout von echten Fehlercodes und protokollieren Sie die Ursache |
| Beispiel funktioniert lokal, aber nicht im Workflow | Callback, Formularfeld oder die Token-Übergabe fehlt in der echten Zielkette | Prüfen Sie den genauen Übergabepfad vom Solver bis zur finalen Zielanfrage – vom Token bis zum abgesendeten Formular |
Häufige Fragen
Kann ich CaptchaAI testen, ohne meinen Produktionsverkehr zu gefährden?
Ja. Im Schattenmodus läuft CaptchaAI zunächst nur mit, ohne Ihre echten Anfragen zu beeinflussen. Erst wenn die Kennzahlen stimmen, leiten Sie über den TrafficSplitter schrittweise 5 %, 25 % und mehr des Verkehrs um – jederzeit mit Fallback.
Welche CAPTCHA-Typen kann ich im Parallellauf vergleichen?
CaptchaAI löst reCAPTCHA v2 und v3, Cloudflare Turnstile und Challenge, GeeTest v3 sowie Bild-, OCR- und Raster-CAPTCHAs; CaptchaFox, Friendly Captcha und Lemin sind in der Beta. Testen Sie die Typen, die in Ihrem Traffic vorkommen, und gewichten Sie die Ergebnisse nach Ihrer realen Verteilung.
Was kostet CaptchaAI während der Testphase?
CaptchaAI rechnet Thread-basiert ab, nicht pro Lösung: Der Tarif BASIC (15 $/Monat, 5 Threads) genügt für die meisten Paralleltests. Da jeder Thread unbegrenzt viele Lösungen im Abrechnungsmonat abdeckt, bleiben die Kosten unabhängig von der Zahl der Läufe planbar.
Muss ich meinen Integrationscode für die Migration umschreiben?
In den meisten Fällen nicht. CaptchaAI nutzt die vertrauten in.php- und res.php-Endpunkte; im Code tauschen Sie im Wesentlichen Basis-URL und API-Schlüssel aus. Deshalb reicht im Parallellauf dieselbe CaptchaProvider-Klasse für beide Dienste.
Verwandte Leitfäden
- Warum Teams von 2Captcha zu CaptchaAI wechseln
- Umstieg von Anti-Captcha auf CaptchaAI
- CaptchaAI-Schnellstart in wenigen Minuten