API-Tutorials

reCAPTCHA v2 mit Callback-Funktion per API lösen

Sie haben das reCAPTCHA v2 gelöst, das Token sauber in das versteckte Feld g-recaptcha-response geschrieben – und trotzdem passiert nichts. Kein Formularversand, keine Weiterleitung, keine Reaktion. In den meisten dieser Fälle erwartet die Seite kein ausgefülltes Feld, sondern einen Callback: eine JavaScript-Funktion, die Sie mit dem gelösten Token aufrufen müssen.

Die gute Nachricht: Der API-Aufruf an CaptchaAI bleibt exakt derselbe wie bei Standard-reCAPTCHA-v2. Nur der letzte Schritt – die Token-Übergabe – ändert sich. Diese Anleitung führt Sie durch drei Aufgaben:

  • eine Callback-Implementierung im Seitenquelltext erkennen,
  • das CAPTCHA über die CaptchaAI-API lösen,
  • den Callback mit dem gelösten Token auslösen – statt g-recaptcha-response zu setzen.

Neu bei reCAPTCHA v2? Starten Sie mit So lösen Sie reCAPTCHA v2 per API für den Standardablauf und kommen Sie anschließend für die Callback-Variante hierher zurück.


Voraussetzungen

Bevor Sie starten, sollten Sie folgende fünf Dinge zur Hand haben:

  • CaptchaAI-API-Schlüssel – erhältlich unter captchaai.com/api.php, eine 32-stellige Zeichenkette.
  • URL der Zielseite – die vollständige URL, unter der das reCAPTCHA-v2-Widget geladen wird.
  • reCAPTCHA-v2-Sitekey – der öffentliche Schlüssel, der an die Widget-Instanz gebunden ist.
  • Browser-Automatisierung – Selenium, Puppeteer oder Playwright; Sie brauchen JavaScript-Ausführung, um den Callback aufzurufen.
  • Name der Callback-Funktion – die JavaScript-Funktion, über die die Seite das Token entgegennimmt.

Callback oder Standard-v2: wo der Unterschied liegt

Bevor Sie mit dem Erkennen und Lösen beginnen, hilft es, die eine Stelle zu kennen, an der sich beide Varianten überhaupt unterscheiden. Der API-Aufruf an CaptchaAI ist nämlich identisch – der einzige Unterschied ist, was Sie mit dem Token tun, nachdem Sie es erhalten haben.

Schritt Standard-v2 Callback-v2
1. An CaptchaAI übermitteln method=userrecaptcha + Sitekey + Page-URL identisch
2. Ergebnis abfragen action=get + Captcha-ID identisch
3. Token empfangen gleiches Token-Format identisch
4. Token übergeben Feldwert g-recaptcha-response setzen Callback-Funktion mit dem Token aufrufen
5. Formular absenden Formularversand auslösen meist automatisch – der Callback übernimmt das

Wichtig: Setzen Sie bei Callback-Implementierungen niemals g-recaptcha-response. Die Seite ignoriert dieses Feld und wartet darauf, dass die Callback-Funktion ausgelöst wird. Wenn Sie nur das Feld setzen, ohne den Callback aufzurufen, wirkt es für die Seite so, als wäre das CAPTCHA nie gelöst worden.


So erkennen Sie eine Callback-Implementierung

Standard-reCAPTCHA-v2 schreibt das gelöste Token in ein verstecktes g-recaptcha-response-Textfeld. Callback-Implementierungen überspringen diesen Schritt und rufen direkt eine JavaScript-Funktion auf. Der Name dieser Funktion steht typischerweise an einer dieser drei Stellen:

  • im Attribut data-callback des Widget-div,
  • als Eigenschaft callback in einem grecaptcha.render()-Aufruf,
  • tief in der internen Laufzeitkonfiguration ___grecaptcha_cfg.

Methode 1: Das Attribut data-callback prüfen

Sehen Sie sich das reCAPTCHA-Widget-div im Seitenquelltext an:

<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
     data-callback="SubmitToken">
</div>

Ist data-callback vorhanden, arbeitet die Seite mit einem Callback. Der Wert (SubmitToken) ist der Funktionsname, den Sie brauchen.

Methode 2: grecaptcha.render()-Aufrufe prüfen

Durchsuchen Sie das Seiten-JavaScript nach grecaptcha.render:

grecaptcha.render('recaptcha-container', {
  sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
  callback: userVerified
});

Die Eigenschaft callback benennt die Funktion – hier userVerified.

Methode 3: Die interne reCAPTCHA-Konfiguration prüfen

Öffnen Sie die Browserkonsole auf der Zielseite und führen Sie aus:

___grecaptcha_cfg.clients[0]

Arbeiten Sie sich durch die Objektstruktur bis zur Eigenschaft callback. Der genaue Pfad hängt von der Seite ab – je nach reCAPTCHA-Version und Minifizierung kann er clients[0].aa.l.callback lauten oder anders aufgebaut sein. Hat die Seite mehrere reCAPTCHA-Instanzen, prüfen Sie clients[1], clients[2] und so weiter.

Erkennungsskript für die Konsole

Führen Sie dies in der Browserkonsole aus, um Callback-Namen automatisch aufzuspüren:

// Check data-callback attributes
document.querySelectorAll('[data-callback]').forEach(el => {
  console.log('data-callback:', el.getAttribute('data-callback'));
});

// Check internal config
if (typeof ___grecaptcha_cfg !== 'undefined') {
  Object.keys(___grecaptcha_cfg.clients).forEach(key => {
    const client = ___grecaptcha_cfg.clients[key];
    console.log(`Client ${key}:`, JSON.stringify(client, null, 2));
  });
}

Callback-basierte Widgets begegnen Ihnen besonders häufig bei Login- und Terminbuchungsformularen – etwa bei Visa- und Behörden-Terminportalen, die im DACH-Raum stark nachgefragt sind. Dort löst nicht der Formular-Button den Versand aus, sondern die Callback-Funktion selbst. Ein kurzer Blick in das Widget-div oder in grecaptcha.render() zeigt Ihnen sofort, ob Sie es mit dieser Variante zu tun haben.


Der Ablauf im Überblick

Vom Extrahieren des Sitekeys bis zum ausgelösten Callback durchläuft die Automatisierung immer dieselben Stationen. Das folgende Schema zeigt den kompletten Weg – inklusive der Warteschleife beim Abfragen:

Page → extract sitekey + pageurl + callback name
                    ↓
      POST to in.php (method=userrecaptcha)
                    ↓
           receive captcha ID
                    ↓
         wait 15–20 seconds
                    ↓
      GET res.php (action=get, id=…)
          ↓                    ↓
   CAPCHA_NOT_READY       status=1 → token
    (wait 5s, retry)            ↓
                     invoke callback(token)
                              ↓
               site processes token automatically

Python-Beispiel (Selenium)

import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
PAGE_URL = "https://example.com/login"
CALLBACK_NAME = "SubmitToken"  # The callback function name from the page

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_recaptcha_v2(api_key, sitekey, pageurl):
    """Submit a reCAPTCHA v2 task and return the solved token."""

    # Step 1: Submit the captcha
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1,
        },
        timeout=30,
    )
    submit_resp.raise_for_status()
    submit_data = submit_resp.json()

    if submit_data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {submit_data}")

    captcha_id = submit_data["request"]
    print(f"Task created — captcha ID: {captcha_id}")

    # Step 2: Wait before first poll
    time.sleep(15)

    # Step 3: Poll for result
    for _ in range(60):
        result_resp = requests.get(
            RESULT_URL,
            params={
                "key": api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            },
            timeout=30,
        )
        result_resp.raise_for_status()
        result_data = result_resp.json()

        if result_data.get("request") == "CAPCHA_NOT_READY":
            time.sleep(5)
            continue

        if result_data.get("status") == 1:
            return result_data["request"]

        raise RuntimeError(f"Polling error: {result_data}")

    raise TimeoutError("reCAPTCHA v2 solve timed out")


def detect_callback_name(driver):
    """Detect the reCAPTCHA callback function name from the page."""

    # Try data-callback attribute first
    callback = driver.execute_script("""
        const el = document.querySelector('[data-callback]');
        if (el) return el.getAttribute('data-callback');
        return null;
    """)
    if callback:
        return callback

    # Try internal reCAPTCHA config
    callback = driver.execute_script("""
        if (typeof ___grecaptcha_cfg === 'undefined') return null;
        const clients = ___grecaptcha_cfg.clients;
        for (const key of Object.keys(clients)) {
            const client = clients[key];
            // Walk the object tree to find a callback function
            const json = JSON.stringify(client);
            const match = json.match(/"callback":"(\\w+)"/);
            if (match) return match[1];
        }
        return null;
    """)
    return callback


# Main workflow
driver = webdriver.Chrome()
driver.get(PAGE_URL)

# Detect the callback name (or use the known name)
detected = detect_callback_name(driver)
callback_name = detected or CALLBACK_NAME
print(f"Using callback: {callback_name}")

# Solve the CAPTCHA
token = solve_recaptcha_v2(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")

# Invoke the callback with the token
driver.execute_script(f"{callback_name}(arguments[0]);", token)
print("Callback invoked — site should process the token automatically")

# Wait for the page to process
time.sleep(3)
driver.quit()

Was der Code macht:

  1. Übermittelt Sitekey und Page-URL mit method=userrecaptcha an in.php – identisch mit Standard-v2.
  2. Fragt res.php alle 5 Sekunden ab, bis das Token bereitsteht.
  3. Ermittelt den Namen der Callback-Funktion aus dem DOM der Seite.
  4. Ruft die Callback-Funktion per execute_script mit dem gelösten Token auf.
  5. Das seiteneigene JavaScript erledigt den Rest – Formularversand, Validierung oder Weiterleitung.

Node.js-Beispiel (Puppeteer)

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://example.com/login";
const CALLBACK_NAME = "SubmitToken";

const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveRecaptchaV2(apiKey, sitekey, pageurl) {
  // Step 1: Submit the captcha
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageurl,
      json: "1",
    }),
  });

  const submitData = await submitResp.json();
  if (submitData.status !== 1) {
    throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
  }

  const captchaId = submitData.request;
  console.log(`Task created — captcha ID: ${captchaId}`);

  // Step 2: Wait before first poll
  await sleep(15_000);

  // Step 3: Poll for result
  for (let i = 0; i < 60; i++) {
    const resultResp = await fetch(
      `${RESULT_URL}?${new URLSearchParams({
        key: apiKey,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );

    const resultData = await resultResp.json();

    if (resultData.request === "CAPCHA_NOT_READY") {
      await sleep(5_000);
      continue;
    }

    if (resultData.status === 1) {
      return resultData.request;
    }

    throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
  }

  throw new Error("reCAPTCHA v2 solve timed out");
}

async function detectCallbackName(page) {
  return page.evaluate(() => {
    // Try data-callback attribute
    const el = document.querySelector("[data-callback]");
    if (el) return el.getAttribute("data-callback");

    // Try internal config
    if (typeof ___grecaptcha_cfg !== "undefined") {
      const clients = ___grecaptcha_cfg.clients;
      for (const key of Object.keys(clients)) {
        const json = JSON.stringify(clients[key]);
        const match = json.match(/"callback":"(\w+)"/);
        if (match) return match[1];
      }
    }

    return null;
  });
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto(PAGE_URL, { waitUntil: "networkidle2" });

  // Detect callback
  const detected = await detectCallbackName(page);
  const callbackName = detected || CALLBACK_NAME;
  console.log(`Using callback: ${callbackName}`);

  // Solve the CAPTCHA
  const token = await solveRecaptchaV2(API_KEY, SITEKEY, PAGE_URL);
  console.log(`Solved token: ${token.slice(0, 80)}...`);

  // Invoke the callback
  await page.evaluate(
    (name, tkn) => {
      window[name](tkn);
    },
    callbackName,
    token
  );
  console.log("Callback invoked — site should process the token automatically");

  await sleep(3_000);
  await browser.close();
})();

Zwei Punkte, die bei der Puppeteer-Variante gern übersehen werden:

  • Der Callback-Name kommt als String aus detectCallbackName; window[name](token) macht daraus die Funktionsreferenz im Seitenkontext.
  • Rufen Sie erst auf, wenn das Widget vollständig registriert ist – waitUntil: "networkidle2" beim goto ist dafür der einfachste Anker.

PHP-Beispiel

Der API-Aufruf ist in PHP identisch. Der Callback-Aufruf braucht einen Browserkontext, deshalb deckt dieses Beispiel nur die serverseitige Lösung ab. Für den Übergabeschritt nutzen Sie ein Headless-Browser-Tool (z. B. PHP WebDriver).

<?php
$apiKey  = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://example.com/login";

// Step 1: Submit
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
    "key"       => $apiKey,
    "method"    => "userrecaptcha",
    "googlekey" => $sitekey,
    "pageurl"   => $pageurl,
    "json"      => 1,
]));

$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
    die("Submit failed: " . $submit);
}

$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";

// Step 2: Wait and poll
sleep(15);

for ($i = 0; $i < 60; $i++) {
    $result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $captchaId,
        "json"   => 1,
    ]));

    $resultData = json_decode($result, true);

    if ($resultData["request"] === "CAPCHA_NOT_READY") {
        sleep(5);
        continue;
    }

    if ($resultData["status"] === 1) {
        $token = $resultData["request"];
        echo "Solved token: " . substr($token, 0, 80) . "...\n";
        // Pass $token to your browser automation to invoke the callback
        break;
    }

    die("Polling error: " . $result);
}

Sobald Sie das Token in PHP haben, übergeben Sie es mit einem Browser-Automatisierungstool (z. B. php-webdriver), das folgenden Aufruf ausführt:

SubmitToken("TOKEN_FROM_CAPTCHAAI");

Typische Fehler

# Fehler Folge Behebung
1 g-recaptcha-response setzen, statt den Callback aufzurufen Die Seite ignoriert das Token – das Formular wird nie abgesendet Callback-Namen ermitteln und mit dem Token aufrufen
2 Falscher Name der Callback-Funktion JavaScript-Fehler: Funktion nicht definiert data-callback, grecaptcha.render() oder die interne Konfiguration erneut prüfen
3 Callback liegt auf einem anderen Client-Index Auf Seiten mit mehreren Widgets wird die falsche reCAPTCHA-Instanz angesprochen ___grecaptcha_cfg.clients[1], clients[2] usw. prüfen
4 Callback aufrufen, bevor die Seite bereit ist Funktion im Seitenkontext noch nicht definiert Vor dem Aufruf auf DOMContentLoaded oder networkidle warten
5 Verschleierten/minifizierten Namen verwenden Der Callback-Name im Quelltext ist entstellt Über die Laufzeit-Browserkonsole die tatsächliche Funktionsreferenz finden
6 Callback-v2 mit Invisible-v2 verwechseln Manche Invisible-Implementierungen nutzen ebenfalls Callbacks Prüfen, ob data-size="invisible" gesetzt ist – falls ja, siehe So lösen Sie reCAPTCHA Invisible per API

Fehlerbehebung

Die meisten Callback-Probleme lassen sich anhand eines einzigen Symptoms eingrenzen. Suchen Sie Ihren Fall in der Tabelle und arbeiten Sie die Behebung ab:

Symptom Ursache und Behebung
Token gelöst, aber die Seite reagiert nicht Häufigste Ursache: Sie setzen g-recaptcha-response, statt den Callback aufzurufen. Prüfen Sie, ob das Widget data-callback oder ein callback in grecaptcha.render() hat – und rufen Sie genau diese Funktion auf.
ReferenceError: SubmitToken is not defined Die Funktion ist noch nicht geladen oder der Name stimmt nicht. Namen über data-callback/interne Konfiguration bestätigen, auf vollständiges Laden der Seite warten und auf minifizierten Seiten window.SubmitToken in der Konsole prüfen.
Token funktioniert bei Standard-v2, scheitert aber hier Wahrscheinlich eine Callback-Implementierung. Mit den Erkennungsschritten oben bestätigen und dann auf den Callback-Aufruf wechseln.
ERROR_BAD_TOKEN_OR_PAGEURL Das Paar aus Sitekey und Page-URL ist ungültig. Ein reiner API-Fehler, unabhängig von Callback vs. Standard – beide Werte erneut von der Seite extrahieren.
Die Seite hat mehrere reCAPTCHA-Widgets Jedes Widget kann seinen eigenen Callback haben. Jedes g-recaptcha-div bzw. ___grecaptcha_cfg.clients prüfen und das Widget dem Formular zuordnen, das Sie ansteuern.
ERROR_CAPTCHA_UNSOLVABLE Die Abfrage konnte nicht gelöst werden. Mit einer neuen Anfrage wiederholen – der Fehler ist nicht callback-spezifisch.

Die vollständige Fehlerreferenz finden Sie unter Häufige Fehler beim Lösen von reCAPTCHA v2.


Warum CaptchaAI hier funktioniert

Faktor Detail
Gleicher API-Aufruf Submit- und Abfrage-Ablauf sind identisch mit Standard-reCAPTCHA-v2 – keine zusätzlichen Parameter nötig
Erfolgsquote Hohe Erfolgsquote auf unterstützten Typen; Callback und Standard nutzen denselben Solver
Lösungszeit reCAPTCHA v2 typischerweise unter 60 Sekunden
Token-Kompatibilität Das zurückgegebene Token funktioniert sowohl mit der g-recaptcha-response-Übergabe als auch mit dem Callback-Aufruf
Preise Thread-basierte Pläne ab 15 $/Monat (BASIC, 5 Threads) mit unbegrenzten Lösungen pro Thread

Fazit: Das von CaptchaAI zurückgegebene Token ist dasselbe, egal wie die Seite reCAPTCHA v2 implementiert. Der Unterschied liegt vollständig in Ihrem clientseitigen Code – darin, wie Sie das Token an die Seite übergeben.


Vollständiges Beispielprojekt

Wenn Sie kein Gerüst zusammensetzen, sondern direkt ein lauffähiges Projekt starten möchten, deckt das Repository den kompletten Ablauf ab:

  • Umgebungs-Setup und Abhängigkeiten,
  • Polling mit sauberer Wiederholungslogik,
  • Fehlerbehandlung für abgelaufene Tokens und API-Fehler.

Das vollständige lauffähige Beispiel auf GitHub ansehen →


FAQ

Warum passiert nichts, obwohl mein Token gültig ist?

Weil die Seite einen Callback erwartet. Ein gültiges Token allein reicht nicht – solange Sie es nur in g-recaptcha-response schreiben, bleibt es wirkungslos. Rufen Sie stattdessen die registrierte Callback-Funktion mit dem Token auf, dann verarbeitet die Seite das Ergebnis.

Wie lange ist ein gelöstes reCAPTCHA-Token gültig?

Etwa 120 Sekunden. reCAPTCHA-Tokens laufen nach rund zwei Minuten ab. Übergeben Sie das Token deshalb unmittelbar nach dem Lösen an den Callback und lagern Sie es nicht zwischen – ein abgelaufenes Token weist die Seite zurück.

Wie unterscheide ich Callback-v2 von Invisible reCAPTCHA v2?

Am Attribut data-size – zwei Fälle sind zu unterscheiden:

  • data-size="invisible" gesetzt: Invisible-v2, das oft ebenfalls mit einem Callback arbeitet.
  • Attribut fehlt, aber data-callback vorhanden: sichtbares Callback-v2.

Der Weg über die API ist in beiden Fällen gleich; nur der Auslöser unterscheidet sich.

Was tue ich, wenn eine Seite mehrere reCAPTCHA-Widgets hat?

Behandeln Sie jedes Widget einzeln – Schritt für Schritt:

  • Prüfen Sie ___grecaptcha_cfg.clients auf alle registrierten Instanzen.
  • Ordnen Sie jede Instanz ihrem Formular zu; jede kann einen eigenen Callback und einen eigenen Client-Index haben.
  • Rufen Sie nur den Callback auf, der zum anvisierten Formular gehört.

Jetzt mit dem Callback-Solving starten

  1. API-Schlüssel holencaptchaai.com/api.php
  2. Callback-Namen ermittelndata-callback, grecaptcha.render() oder die interne Konfiguration prüfen
  3. Python- oder Node.js-Code oben kopieren – Platzhalter durch Ihren Schlüssel, Sitekey, Page-URL und Callback-Namen ersetzen
  4. Ausführen – das Token kommt typischerweise in unter 60 Sekunden, der Callback feuert, die Seite verarbeitet das Ergebnis
  5. Hängen geblieben? Starten Sie mit Häufige Fehler beim Lösen von reCAPTCHA v2 oder lesen Sie die vollständigen CaptchaAI-API-Dokumente

Verwandte Artikel

Kommentare sind für diesen Artikel deaktiviert.