CAPTCHA-Last kommt selten gleichmäßig: mal keine Anfrage, dann Hunderte in kurzer Zeit. Für dieses Profil ist AWS Lambda ideal – Sie zahlen nur beim Lösen und skalieren automatisch von null auf Hunderte parallele Aufrufe.
Dieser Leitfaden betreibt CaptchaAI serverlos hinter API Gateway und SQS. Die Beispiele lösen reCAPTCHA v2, gelten aber ebenso für v3, Turnstile, GeeTest v3 und Bild-CAPTCHAs.
Wann sich der serverlose Ansatz lohnt
Serverlos ist nicht für jede Last die beste Wahl: Bei konstant hohem, gleichmäßigem Durchsatz kann ein dauerhaft laufender Worker günstiger sein. Seine Stärken spielt Lambda genau dann aus, wenn Ihr Traffic schwankt:
- Unregelmäßige Spitzen – Scraping-Jobs, die nur nachts laufen, oder Formular-Flows mit saisonalen Ausschlägen.
- Keine Grundlast – Sie zahlen nichts, solange keine Anfrage eintrifft, statt einen VPS im Leerlauf zu finanzieren.
- Schnelle Skalierung – von null auf Hunderte parallele Aufrufe in Sekunden, ohne Kapazität vorzuhalten.
- Wenig Betrieb – kein Patchen, kein Autoscaling-Setup, keine eigenen Health-Checks.
Der eigentliche Engpass ist am Ende nicht Lambda, sondern Ihr CaptchaAI-Thread-Kontingent – dazu weiter unten mehr.
Der Lambda-Handler: Aufgabe übermitteln, Token abfragen
Der Handler übermittelt die Aufgabe an in.php und fragt anschließend res.php ab, bis das Token vorliegt. Für diesen einfachen HTTP-Ablauf genügt urllib aus der Python-Standardbibliothek – das erspart Ihnen zusätzliche Abhängigkeiten und Lambda-Layer. Der Handler liest method und params aus dem Request-Body, sodass sich derselbe Code für jeden unterstützten CAPTCHA-Typ wiederverwenden lässt.
# lambda_function.py
import json
import os
import time
import urllib.request
import urllib.parse
def lambda_handler(event, context):
"""AWS Lambda handler for CaptchaAI solving."""
api_key = os.environ["CAPTCHAAI_KEY"]
# Parse input
body = json.loads(event.get("body", "{}")) if isinstance(event.get("body"), str) else event
method = body.get("method", "userrecaptcha")
params = body.get("params", {})
try:
token = solve_captcha(api_key, method, params)
return {
"statusCode": 200,
"body": json.dumps({"token": token}),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)}),
}
def solve_captcha(api_key, method, params, timeout=90):
"""Solve CAPTCHA using CaptchaAI API."""
# Submit task
submit_data = urllib.parse.urlencode({
"key": api_key,
"method": method,
"json": 1,
**params,
}).encode()
req = urllib.request.Request(
"https://ocr.captchaai.com/in.php",
data=submit_data,
)
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
if result.get("status") != 1:
raise RuntimeError(f"Submit error: {result.get('request')}")
task_id = result["request"]
# Poll for result
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
poll_url = (
f"https://ocr.captchaai.com/res.php"
f"?key={api_key}&action=get&id={task_id}&json=1"
)
with urllib.request.urlopen(poll_url, timeout=15) as resp:
data = json.loads(resp.read())
if data["request"] != "CAPCHA_NOT_READY":
if data.get("status") == 1:
return data["request"]
raise RuntimeError(f"Solve error: {data['request']}")
raise TimeoutError("Solve timeout")
API-Schlüssel sicher verwalten mit Secrets Manager
In Produktion gehört der Schlüssel nicht als Klartext in eine Umgebungsvariable, sondern in den AWS Secrets Manager – dort ist der Zugriff per IAM steuerbar, rotierbar und auditierbar. Die Funktion liest das Secret erst zur Laufzeit; der Schlüssel taucht weder im Repository noch im Deployment-Log auf.
import json
import boto3
def get_api_key():
"""Retrieve CaptchaAI key from AWS Secrets Manager."""
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="captchaai/api-key")
secret = json.loads(response["SecretString"])
return secret["api_key"]
Secret anlegen:
aws secretsmanager create-secret \
--name captchaai/api-key \
--secret-string '{"api_key":"YOUR_API_KEY"}'
Infrastruktur als Code: das SAM-Template
Das SAM-Template beschreibt Funktion, API-Route und Berechtigungen in einer einzigen, versionierbaren Datei. Hier setzen Sie den Timeout auf 120 Sekunden, die POST-Route /solve und eine eng gefasste IAM-Richtlinie, die ausschließlich Lesezugriff auf genau dieses eine Secret erlaubt.
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 120
MemorySize: 256
Runtime: python3.11
Resources:
CaptchaSolverFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Environment:
Variables:
CAPTCHAAI_KEY: !Sub "{{resolve:secretsmanager:captchaai/api-key:SecretString:api_key}}"
Events:
SolveApi:
Type: Api
Properties:
Path: /solve
Method: post
Policies:
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:captchaai/api-key-*"
Outputs:
SolveApiUrl:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/solve"
Deployment mit sam build und sam deploy
sam build verpackt den Code, sam deploy --guided fragt beim ersten Mal Region, Stack-Namen und Bestätigungen ab und legt die Werte in samconfig.toml ab – danach genügt ein schlichtes sam deploy. Der abschließende curl-Aufruf prüft die Route direkt gegen Ihr API Gateway.
# Build and deploy
sam build
sam deploy --guided
# Test
curl -X POST https://YOUR_API_ID.execute-api.us-east-1.amazonaws.com/Prod/solve \
-H "Content-Type: application/json" \
-d '{
"method": "userrecaptcha",
"params": {
"googlekey": "SITE_KEY",
"pageurl": "https://example.com"
}
}'
Stapelverarbeitung über SQS
Für Massenverarbeitung entkoppelt eine SQS-Warteschlange das Einreichen vom Lösen: Lambda zieht Aufgaben in Batches, verarbeitet sie parallel und schreibt die Ergebnisse zurück. Fehlgeschlagene Nachrichten wandern über eine Dead-Letter-Queue in einen erneuten Versuch, statt verloren zu gehen – praktisch für Scraping-Pipelines mit tausenden Formularen.
import json
import os
import time
import urllib.request
import urllib.parse
def sqs_handler(event, context):
"""Process CAPTCHA tasks from SQS queue."""
api_key = os.environ["CAPTCHAAI_KEY"]
results = []
for record in event["Records"]:
task = json.loads(record["body"])
try:
token = solve_captcha(
api_key,
task["method"],
task["params"],
)
results.append({
"task_id": task.get("id"),
"status": "success",
"token": token[:50],
})
except Exception as e:
results.append({
"task_id": task.get("id"),
"status": "error",
"error": str(e),
})
return {"results": results}
Lambda richtig dimensionieren: Timeout, Speicher, Parallelität
Da der Solver fast nur wartet, reichen 256 MB – der Timeout ist die wichtigste Stellschraube.
| Faktor | Wert |
|---|---|
| Max. Timeout | 15 Minuten (für die meisten CAPTCHAs auf 2 Minuten eingestellt) |
| Speicher | 256 MB ausreichend (keine schwere Verarbeitung) |
| Parallelität | Standardmäßig 1000 gleichzeitig (bei Bedarf Erhöhung anfordern) |
| Kaltstart | ~500 ms für Python (vernachlässigbar im Vergleich zur Lösungszeit) |
| Kosten | ~0,0001 $ pro Lösung (nur Berechnung) |
| Abhängigkeiten | Verwenden Sie urllib (integriert), um Lambda-Ebenen zu vermeiden |
DACH-Kontext: Ein dauerhaft laufender Hetzner- oder netcup-VPS kostet auch im Leerlauf – Lambda skaliert dagegen auf null. Wichtiger als die AWS-Rechnung ist aber die Thread-basierte Abrechnung von CaptchaAI: Sie zahlen pro gleichzeitigem Thread, nicht pro Lösung. Ihre effektive Parallelität sollte deshalb zur Thread-Anzahl Ihres Plans passen – etwa BASIC (15 $/Monat, 5 Threads) oder ADVANCE (90 $/Monat, 50 Threads). Mit Reserved Concurrency auf der Lambda-Funktion starten Sie nie mehr parallele Aufrufe, als Ihr Plan an Threads bereitstellt.
Häufige Fehler und ihre Lösung
| Problem | Ursache | Lösung |
|---|---|---|
| Funktion läuft ab | Lambda-Timeout < Lösungszeit | Stellen Sie den Timeout auf 120s+ ein |
| Zugriff auf Secret verweigert | Fehlende IAM-Richtlinie | Fügen Sie die SecretsManager-Leserichtlinie hinzu |
| Kaltstart erhöht die Latenz | Seltene Aufrufe | Nutzen Sie Provisioned Concurrency |
Importfehler für requests |
Nicht in Lambda gebündelt | Verwenden Sie urllib.request (integriert) oder fügen Sie eine Ebene hinzu |
Häufige Fragen
Wie viele Lambda-Aufrufe darf ich parallel laufen lassen?
So viele, wie Ihr CaptchaAI-Plan an Threads bereitstellt – BASIC bietet 5, ADVANCE 50 gleichzeitige Solves. Lambda erlaubt standardmäßig 1000 parallele Ausführungen; der Engpass ist Ihr Thread-Kontingent.
Wie speichere ich meinen API-Schlüssel sicher?
Über den AWS Secrets Manager, nicht als Klartext. Das SAM-Template löst ihn zur Laufzeit auf; eine IAM-Richtlinie beschränkt den Zugriff.
Funktioniert derselbe Handler für reCAPTCHA v3 und Turnstile?
Ja. Ändern Sie nur method und params – userrecaptcha für reCAPTCHA v2/v3, turnstile für Turnstile, geetest für GeeTest v3. Die Poll-Logik bleibt identisch.
Wie reduziere ich Kaltstarts bei seltenem Traffic?
Mit Provisioned Concurrency halten Sie Instanzen warm, sodass der Kaltstart von rund 500 ms bei Python entfällt. Bei gelegentlichen Jobs fällt er gegenüber der Lösungszeit kaum ins Gewicht – dann ist Provisioned Concurrency den Aufpreis meist nicht wert.
Zählt jeder Lambda-Aufruf als eigener CaptchaAI-Thread?
Nur, solange er tatsächlich gleichzeitig läuft. Ein Thread ist eine in Arbeit befindliche Lösung; sobald ein Solve fertig ist, wird der Thread frei und nimmt die nächste Aufgabe. Begrenzen Sie die Reserved Concurrency Ihrer Funktion auf Ihre Thread-Anzahl, um Fehler durch überschrittene Parallelität zu vermeiden.
Verwandte Leitfäden
Bereit für Serverless? Sichern Sie sich Ihren CaptchaAI-Schlüssel und lösen Sie Ihr erstes CAPTCHA in Lambda.