Wenn das per API gelöste Token in einem requests-Skript sauber durchläuft, im automatisierten Browser aber abgelehnt wird, liegt das fast nie am Token selbst. Meist wurde der Callback nicht ausgelöst oder das Token war beim Absenden bereits abgelaufen. Diese Anleitung nimmt die sechs häufigsten Ursachen der Reihe nach auseinander – von der Callback-Auslösung über das Timing bis zur Bot-Erkennung jenseits des CAPTCHAs.
Ein typisches Muster aus DACH-Teams: Der API-Integrationstest in der GitLab-CI-Pipeline ist grün, doch im echten Selenium-Browser bleibt der Absenden-Button deaktiviert. Genau diese Lücke schließen die folgenden Abschnitte.
Die drei Ursachen, die in der Praxis am häufigsten dahinterstecken:
- Callback nicht ausgelöst – das Token liegt im Feld, aber das Formular erfährt nie davon.
- Token abgelaufen – zwischen Lösung und Absenden vergehen mehr als rund 120 Sekunden.
- Falsche Page-URL – an die API ging eine andere URL als die, auf der der Browser tatsächlich steht.
Wo die Token-Übergabe typischerweise scheitert
| Problem | Symptom | Grundursache |
|---|---|---|
| Token landet nicht im richtigen Element | Formular wird ohne Token gesendet | Falscher Textarea-Selektor |
| Token vor dem Absenden abgelaufen | Seite zeigt erneut ein CAPTCHA | Zu viel Zeit zwischen Lösen und Absenden |
| Callback nicht ausgelöst | Absenden-Button bleibt deaktiviert | Fehlender grecaptcha-Callback |
| Falsche Page-URL an die API übergeben | Token für diese Domain ungültig | URL stimmt nicht überein |
| Bot-Erkennung jenseits des CAPTCHAs | Sperre, obwohl das Token akzeptiert wurde | Browser-Fingerprinting |
In welcher Reihenfolge Sie prüfen sollten
Arbeiten Sie die Ursachen nicht wahllos ab, sondern von der wahrscheinlichsten zur seltensten. In den meisten DACH-Support-Fällen steckt der Fehler bereits in den ersten beiden Punkten – das spart Ihnen den Umweg über aufwendige Browser-Analysen:
- Prüfen Sie, ob der Callback nach dem Einfügen des Tokens tatsächlich feuert – das ist mit Abstand die häufigste Ursache.
- Messen Sie die Zeitspanne zwischen Lösung und Absenden; ein reCAPTCHA-v2-Token verfällt nach rund 120 Sekunden.
- Vergleichen Sie die an die API übergebene Page-URL mit dem, was
driver.current_urltatsächlich liefert. - Kontrollieren Sie erst zuletzt eine nachgelagerte Bot-Erkennung – sie greift seltener, als in der Praxis vermutet wird.
Hinweis: Die Techniken in dieser Anleitung dienen der Fehlersuche in Ihren eigenen oder ausdrücklich autorisierten Automatisierungen. Testen Sie Token-Handling und Formular-Submits ausschließlich gegen Systeme, für die Sie die Berechtigung haben.
Schnelldiagnose nach Symptom
Ordnen Sie Ihr Symptom einer Ursache zu und springen Sie direkt zur passenden Lösung weiter unten:
| Symptom | Ursache | Fix |
|---|---|---|
| Token gesetzt, Formular wird trotzdem abgelehnt | Callback nicht ausgelöst | Callback-Funktion suchen und aufrufen |
| Absenden-Button bleibt deaktiviert | Erst der Callback aktiviert ihn | Callback nach dem Einfügen des Tokens auslösen |
| „Ungültiges CAPTCHA" nach dem Absenden | Token abgelaufen | Zeit zwischen Lösung und Übermittlung verkürzen |
| 403 nach dem Absenden des Formulars | Bot-Erkennung (kein CAPTCHA) | Browser-Flags und Proxy-Konfiguration prüfen |
Läuft mit requests, scheitert im Browser |
Abweichende Page-URL | driver.current_url als Page-URL verwenden |
Lösung 1: reCAPTCHA-v2-Token korrekt einfügen und Callback auslösen
Das Token muss in die Textarea g-recaptcha-response eingetragen werden UND der Callback muss feuern – erst beides zusammen macht die Übermittlung gültig:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def inject_recaptcha_token(driver, token):
"""Properly inject reCAPTCHA v2 token in browser."""
# Step 1: Make the response textarea visible and set the value
driver.execute_script("""
// Find all response textareas (may be multiple on page)
var textareas = document.querySelectorAll('[name="g-recaptcha-response"]');
textareas.forEach(function(ta) {
ta.style.display = 'block';
ta.value = arguments[0];
});
// Also set via ID if present
var byId = document.getElementById('g-recaptcha-response');
if (byId) {
byId.style.display = 'block';
byId.value = arguments[0];
}
""", token)
# Step 2: Trigger the callback
driver.execute_script("""
// Try standard callback
if (typeof ___grecaptcha_cfg !== 'undefined') {
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {
var client = clients[key];
// Navigate nested structure to find callback
for (var prop in client) {
var val = client[prop];
if (val && typeof val === 'object') {
for (var subprop in val) {
var subval = val[subprop];
if (subval && typeof subval === 'object' && subval.callback) {
subval.callback(arguments[0]);
return;
}
}
}
}
}
}
// Fallback: try common callback names
if (typeof onCaptchaSuccess === 'function') onCaptchaSuccess(arguments[0]);
else if (typeof captchaCallback === 'function') captchaCallback(arguments[0]);
""", token)
# Usage
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# ... solve CAPTCHA via CaptchaAI ...
inject_recaptcha_token(driver, token)
time.sleep(1)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
Der häufigste Fehler hier: Das Token wird gesetzt, aber Schritt 2 fehlt – ohne ausgelösten Callback bleibt die Seite in ihrem „ungelöst"-Zustand.
Lösung 2: Formulare mit Callback-Abhängigkeit freischalten
Manche Formulare bleiben deaktiviert, bis der CAPTCHA-Callback feuert – das Setzen des Tokens allein reicht dann nicht:
def find_and_trigger_callback(driver, token):
"""Find the reCAPTCHA callback and trigger it."""
# Method 1: Extract callback from data attribute
callback_name = driver.execute_script("""
var widget = document.querySelector('.g-recaptcha');
if (widget) {
return widget.getAttribute('data-callback');
}
return null;
""")
if callback_name:
driver.execute_script(f"window['{callback_name}'](arguments[0]);", token)
return True
# Method 2: Extract from grecaptcha config
triggered = driver.execute_script("""
try {
var widgetId = 0;
var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
if (typeof callback === 'function') {
callback(arguments[0]);
return true;
}
} catch(e) {}
return false;
""", token)
return triggered
Lösung 3: Timing – Token sofort nach dem Lösen einsetzen
Ein reCAPTCHA-v2-Token ist nur rund 120 Sekunden gültig. Wer zwischen Lösung und Absenden zu lange wartet, sendet ein bereits abgelaufenes Token. Fragen Sie das Ergebnis daher zügig ab und fügen Sie es unmittelbar ein:
import requests
import time
def solve_and_inject_fast(driver, api_key, sitekey, page_url):
"""Solve CAPTCHA and inject token with minimal delay."""
# Submit solve request
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": 1,
}, timeout=30)
task_id = resp.json()["request"]
# Poll for result
time.sleep(15)
for _ in range(24):
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
token = data["request"]
# IMMEDIATELY inject — don't wait
inject_recaptcha_token(driver, token)
# Submit form within 5 seconds
time.sleep(0.5)
return True
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(data["request"])
time.sleep(5)
raise TimeoutError("Solve timeout")
Lösung 4: Die richtige Page-URL bei SPAs und Iframes
Bei Single-Page-Apps und iframe-gebundenen Widgets weicht die tatsächlich geladene URL oft von der ursprünglich angesteuerten ab. Übergeben Sie an die API die URL, die der Browser gerade wirklich anzeigt – nicht die, zu der Sie navigiert haben:
def get_correct_pageurl(driver):
"""Get the URL that reCAPTCHA actually sees."""
# Check if CAPTCHA is in an iframe
iframes = driver.find_elements(By.TAG_NAME, "iframe")
for iframe in iframes:
src = iframe.get_attribute("src") or ""
if "recaptcha" in src or "anchor" in src:
# CAPTCHA is in iframe — use parent page URL
return driver.current_url
# For SPAs, use current URL (may differ from initial load)
return driver.current_url
# WRONG — using the URL you navigated to
pageurl = "https://example.com/form" # May have redirected
# CORRECT — using the URL the browser is actually on
pageurl = get_correct_pageurl(driver)
Lösung 5: Wenn die Website trotz gültigem Token blockiert
Manchmal ist das Token einwandfrei, und die Seite lehnt die Anfrage trotzdem ab. Dann greift keine CAPTCHA-Prüfung mehr, sondern eine nachgelagerte Bot-Erkennung, die auf Automatisierungsmerkmale des Browsers reagiert. Ein realistisch konfigurierter Browser mit stabilen Startparametern reduziert diese Fehlalarme:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
def create_browser_driver():
"""Create a browser that avoids basic bot detection."""
options = Options()
# Remove automation indicators
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
# Set realistic window size
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
return driver
driver = create_browser_driver()
Zur Einordnung: CaptchaAI löst die CAPTCHA-Abfrage. Ob eine nachgelagerte Prüfung Ihre Anfrage akzeptiert, hängt vom Umfeld ab – testen Sie ausschließlich in eigenen oder autorisierten Umgebungen.
Lösung 6: Cloudflare-Turnstile-Token einfügen
Cloudflare Turnstile nutzt ein eigenes Antwortfeld (cf-turnstile-response) und erwartet zusätzlich ein ausgelöstes change-Ereignis:
def inject_turnstile_token(driver, token):
"""Inject Turnstile token into the page."""
driver.execute_script("""
// Find Turnstile response input
var inputs = document.querySelectorAll(
'input[name="cf-turnstile-response"], ' +
'input[name="g-recaptcha-response"]'
);
inputs.forEach(function(input) {
input.value = arguments[0];
});
// Trigger change event
inputs.forEach(function(input) {
input.dispatchEvent(new Event('change', { bubbles: true }));
input.dispatchEvent(new Event('input', { bubbles: true }));
});
// Try Turnstile callback
if (window.turnstile) {
var widgets = document.querySelectorAll('[data-callback]');
widgets.forEach(function(w) {
var cb = w.getAttribute('data-callback');
if (typeof window[cb] === 'function') {
window[cb](arguments[0]);
}
});
}
""", token)
Debug-Checkliste: Seitenzustand ausgeben, bevor Sie raten
Wenn keiner der Fixes greift, lassen Sie sich den Zustand der Seite ausgeben – ob die Textarea existiert, ein Wert gesetzt ist, grecaptcha geladen wurde und welcher Callback registriert ist:
def debug_captcha_injection(driver, token):
"""Print debug info to diagnose injection failures."""
info = driver.execute_script("""
var result = {};
// Check textarea exists
var ta = document.querySelector('[name="g-recaptcha-response"]');
result.textarea_found = !!ta;
result.textarea_value_set = ta ? ta.value.length > 0 : false;
// Check reCAPTCHA loaded
result.grecaptcha_loaded = typeof grecaptcha !== 'undefined';
// Check for callback
var widget = document.querySelector('.g-recaptcha');
result.has_data_callback = widget ? !!widget.getAttribute('data-callback') : false;
result.callback_name = widget ? widget.getAttribute('data-callback') : null;
// Current URL
result.current_url = window.location.href;
// Check for errors in console (basic)
result.has_submit_button = !!document.querySelector('button[type=submit], input[type=submit]');
return result;
""")
for key, value in info.items():
print(f" {key}: {value}")
return info
Häufige Fragen
Warum bleibt der Absenden-Button trotz gültigem Token deaktiviert?
Weil der Callback fehlt. Viele Formulare aktivieren den Button erst, wenn die Callback-Funktion aufgerufen wurde – das reine Setzen von g-recaptcha-response genügt nicht. Lösen Sie den Callback nach dem Einfügen des Tokens explizit aus (Lösung 1 und 2).
Wie schnell muss ich ein reCAPTCHA-v2-Token nach dem Lösen einsetzen?
Innerhalb von rund 120 Sekunden. Danach läuft es ab, und die Seite fordert ein neues CAPTCHA an. Fügen Sie das Token direkt nach der Ergebnisabfrage ein und senden Sie das Formular unmittelbar danach.
Wie finde ich den Namen der Callback-Funktion auf einer Seite?
Über das Attribut data-callback des .g-recaptcha-Elements. Ist es leer, lässt sich der Callback über die interne ___grecaptcha_cfg-Struktur auslesen – beide Wege zeigt der Code in Lösung 2.
Warum kommt nach dem Absenden ein 403, obwohl das CAPTCHA akzeptiert wurde?
Dann greift eine Bot-Erkennung unabhängig vom CAPTCHA. Prüfen Sie die Browser-Startparameter und Ihre Proxy-Konfiguration und testen Sie ausschließlich in eigenen oder autorisierten Umgebungen.
Gilt derselbe Ansatz auch für Playwright oder Puppeteer?
Ja. Das Prinzip bleibt identisch: Token in g-recaptcha-response eintragen, danach den Callback auslösen. Nur die Methode zum Ausführen von JavaScript unterscheidet sich – in Playwright page.evaluate(), in Puppeteer page.evaluate() statt driver.execute_script().
Verwandte Leitfäden
Weiterführende Ressourcen
- CaptchaAI Schnellstart
- API-Antwortformate und Fehlercodes
- reCAPTCHA v2 per API lösen