API-Tutorials

So lösen Sie GeeTest v3 mithilfe der API

GeeTest v3 kommt ohne den einzelnen Sitekey aus, den man von reCAPTCHA kennt: Die Verifizierung hängt an den Werten gt und challenge, und zurück an das Formular gehen am Ende gleich drei Felder. Über die CaptchaAI-API läuft das so:

  1. gt und challenge aus der Seite auslesen
  2. Aufgabe mit method=geetest an CaptchaAI übermitteln
  3. Ergebnis über die Task-ID abfragen
  4. geetest_challenge, geetest_validate und geetest_seccode mit dem Formular absenden

Für den Lösungsvorgang selbst nennt CaptchaAI eine Obergrenze von unter 12 Sekunden pro GeeTest-v3-Abfrage – der eigentliche Stolperstein in produktiven Integrationen liegt fast immer im ersten Schritt. Der Wert gt ist pro Anwendung fest verdrahtet, challenge gilt für genau eine Verifizierung. Wer diesen Unterschied überliest, baut eine Integration, die im ersten Testlauf sauber durchläuft und beim zweiten ERROR_CAPTCHA_UNSOLVABLE zurückliefert.

Dieser Artikel zeigt den vollständigen Ablauf mit Python und Node.js – samt der Fehlerbilder, die in echten Test-Pipelines auftreten.

Was Sie vorab brauchen

  • CaptchaAI API-Schlüssel – aus dem Dashboard auf captchaai.com
  • gt-Wert – feste Kennung pro Anwendung, einmal konfiguriert
  • challenge-Wert – pro Sitzung neu und genau einmal gültig
  • Page-URL – die Adresse, auf der GeeTest eingebunden ist
  • Laufzeitumgebung – Python 3.7+ oder Node.js 14+
  • api_server – optional, nur bei abweichendem Verifizierungsserver

Schritt 1: GeeTest-Parameter gt, challenge und api_server auslesen

Drei Parameter gehen in die Anfrage ein. gt bleibt über alle Anfragen hinweg gleich und lässt sich einmalig in die Konfiguration übernehmen; challenge müssen Sie unmittelbar vor jeder Lösung neu holen. Drei Wege führen zum Ziel – in dieser Reihenfolge sind sie am schnellsten geprüft.

Hinweis: Holen Sie challenge immer direkt vor der Übermittlung. Ein Wert, der dazwischen mehrere Minuten liegen bleibt, ist bei der Verifizierung meist verbraucht.

Weg 1: Netzwerk-Tab in den DevTools

  1. Öffnen Sie die DevTools und wechseln Sie in den Tab Netzwerk
  2. Filtern Sie nach register-slide, gettype.php oder get.php
  3. Lösen Sie das CAPTCHA aus und suchen Sie die Initialisierungsanfrage
  4. Die Antwort liefert gt, challenge und mitunter api_server
{
  "success": 1,
  "gt": "019924a82c70bb123aae90d483087f94",
  "challenge": "12345678abc90def12345678abc90def",
  "new_captcha": true
}

Weg 2: Seitenquelltext durchsuchen

Bindet die Anwendung GeeTest direkt im Markup ein, steht gt im Initialisierungsaufruf. Ein kurzer Ausdruck in der Konsole reicht:

// Search page source for initGeetest or gt value
document.querySelectorAll('script').forEach(s => {
  if (s.textContent.includes('initGeetest')) {
    console.log(s.textContent);
  }
});

Weg 3: Registrierungs-Endpunkt der Anwendung

Der stabilste Weg für Automatisierung: Viele Anwendungen holen die Parameter über einen eigenen Endpunkt, den Sie direkt aufrufen können – ganz ohne Browser-Rendering.

# The site's registration endpoint
params_response = requests.get("https://example.com/api/captcha/register")
data = params_response.json()
gt = data["gt"]
challenge = data["challenge"]

Schritt 2: Aufgabe an CaptchaAI übermitteln

Die Übermittlung geht an in.php mit method=geetest, zusammen mit gt, challenge und pageurl. Zurück kommt die Task-ID für den nächsten Schritt. Prüfen Sie status explizit – ein Tippfehler im gt fällt sonst erst beim Polling auf.

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

response = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": API_KEY,
    "method": "geetest",
    "gt": "019924a82c70bb123aae90d483087f94",
    "challenge": "12345678abc90def12345678abc90def",
    "api_server": "api.geetest.com",  # Optional, use if site specifies
    "pageurl": "https://example.com/login",
    "json": 1
})

data = response.json()
if data.get("status") != 1:
    raise Exception(f"Submit error: {data.get('request')}")

task_id = data["request"]
print(f"Task submitted: {task_id}")

Node.js

const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';

async function submitGeeTest(gt, challenge, pageurl) {
  const { data } = await axios.get('https://ocr.captchaai.com/in.php', {
    params: {
      key: API_KEY,
      method: 'geetest',
      gt,
      challenge,
      api_server: 'api.geetest.com',
      pageurl,
      json: 1
    }
  });

  if (data.status !== 1) throw new Error(`Submit error: ${data.request}`);
  return data.request;
}

Schritt 3: Ergebnis abfragen statt blind warten

Das Ergebnis holen Sie über res.php ab. Solange die Aufgabe läuft, antwortet die API mit CAPCHA_NOT_READY; jede andere Meldung ist ein echter Fehler und sollte die Schleife sofort beenden. Ein Intervall von 5 Sekunden hat sich bewährt – häufigeres Abfragen bringt bei GeeTest v3 keinen Zeitgewinn.

Zurück kommen drei Werte: geetest_challenge, geetest_validate und geetest_seccode. Der zurückgegebene challenge unterscheidet sich vom eingereichten – weitergeben müssen Sie die Version aus der Antwort.

Python

def get_geetest_solution(task_id):
    for attempt in range(30):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1
        }).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {result.get('request')}")

    raise Exception("Timeout")

solution = get_geetest_solution(task_id)
# solution = {
#   "geetest_challenge": "12345678abc90def12345678abc90def1a",
#   "geetest_validate": "abcdef1234567890abcdef1234567890",
#   "geetest_seccode": "abcdef1234567890abcdef1234567890|jordan"
# }

Node.js

async function getGeeTestSolution(taskId) {
  for (let i = 0; i < 30; i++) {
    await new Promise(r => setTimeout(r, 5000));
    const { data } = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 }
    });
    if (data.status === 1) return data.request;
    if (data.request !== 'CAPCHA_NOT_READY') throw new Error(data.request);
  }
  throw new Error('Timeout');
}

Schritt 4: challenge, validate und seccode absenden

Alle drei Werte gehören in dieselbe Anfrage wie die übrigen Formulardaten. Fehlt einer davon, weist die Verifizierung ab – auch wenn die beiden anderen stimmen. Senden Sie außerdem zügig ab: Die Werte gelten für genau diese eine Verifizierung und lassen sich nicht auf Vorrat halten.

# Submit the GeeTest solution with the form data
verify_response = requests.post("https://example.com/api/login", data={
    "username": "[email protected]",
    "password": "password123",
    "geetest_challenge": solution["geetest_challenge"],
    "geetest_validate": solution["geetest_validate"],
    "geetest_seccode": solution["geetest_seccode"]
})

print(f"Login status: {verify_response.status_code}")

Der komplette GeeTest-v3-Ablauf in einem Python-Skript

Zusammengesetzt sind es rund 30 Zeilen – kompakt genug für einen Test-Helper:

import requests
import time

API_KEY = "YOUR_API_KEY"
SITE_URL = "https://example.com/login"

# 1. Get GeeTest parameters from the site
params = requests.get("https://example.com/api/captcha/register").json()

# 2. Submit to CaptchaAI
submit = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": API_KEY,
    "method": "geetest",
    "gt": params["gt"],
    "challenge": params["challenge"],
    "pageurl": SITE_URL,
    "json": 1
}).json()
task_id = submit["request"]

# 3. Poll for solution
for _ in range(30):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": 1
    }).json()
    if result.get("status") == 1:
        solution = result["request"]
        break

# 4. Submit to site
login = requests.post(SITE_URL, data={
    "username": "[email protected]",
    "password": "pass",
    "geetest_challenge": solution["geetest_challenge"],
    "geetest_validate": solution["geetest_validate"],
    "geetest_seccode": solution["geetest_seccode"]
})
print(f"Result: {login.status_code}")

Durchsatz planen: Abrechnung pro Thread

Für die Kapazitätsplanung ist eines entscheidend: CaptchaAI rechnet nach gleichzeitigen Threads ab, nicht pro gelöster Abfrage. Ein Thread ist eine laufende Lösung; sobald sie abgeschlossen ist, nimmt derselbe Thread die nächste Aufgabe an. Innerhalb eines Tarifs sind die Lösungen pro Thread unbegrenzt; abgerechnet wird in US-Dollar.

Ein typisches Rechenbeispiel aus dem DACH-Alltag: Eine nächtliche Regressionssuite läuft in GitLab CI auf einem Hetzner-VPS und prüft 400 Login-Vorgänge in der eigenen Staging-Umgebung, jeden davon mit einer GeeTest-v3-Abfrage. Rechnet man konservativ mit der Obergrenze von 12 Sekunden pro Lösung, ergeben sich rund 4.800 Sekunden Thread-Zeit; je nach Tarif verteilt sich das so:

Tarif Threads Dauer des Beispiellaufs
BASIC (15 $/Monat) 5 ~16 Minuten
STANDARD (30 $/Monat) 15 ~5,3 Minuten
ADVANCE (90 $/Monat) 50 unter 2 Minuten

Für einen nächtlichen Job genügt der kleinste Tarif also problemlos. Reserve braucht erst, wer Suiten parallel zum Tages-Traffic laufen lässt oder mehrere Projekte auf denselben Schlüssel legt.

Zwei Punkte gehören in jedes solche Setup: Testen Sie ausschließlich gegen eigene oder freigegebene Umgebungen, und behandeln Sie IP-Adressen aus Proxy- und Logdaten von Beginn an als personenbezogene Daten – die DSGVO-Bewertung gehört in die Architekturentscheidung, nicht in die Nachdokumentation.

Fehler deuten und beheben

Fehler Ursache Lösung
ERROR_BAD_PARAMETERS gt oder challenge fehlt oder ist leer Beide Werte sind Pflicht – vor dem Absenden aus der Seite auslesen
ERROR_CAPTCHA_UNSOLVABLE challenge abgelaufen oder ungültig Frischen challenge-Wert holen und die Aufgabe erneut übermitteln
Anwendung weist die Lösung ab Veralteter oder falscher challenge-Wert challenge ist einmalig gültig; pro Versuch einen neuen Satz verwenden
geetest_validate ist leer Lösungsvorgang intern fehlgeschlagen Mit neuem challenge wiederholen und den Fehlercode protokollieren
Timeout nach 30 Abfragen Polling zu früh beendet oder Netzwerkproblem Intervall bei 5 Sekunden belassen, Gesamtdauer erhöhen, Verbindung prüfen

Vollständig lauffähiges Beispiel

Das Referenzprojekt bringt Umgebungskonfiguration, Polling, Wiederholungslogik und Fehlerbehandlung fertig mit.

Das vollständige ausführbare Beispiel finden Sie auf GitHub →

Häufige Fragen

Warum weist die Anwendung meine Lösung ab, obwohl CaptchaAI status: 1 meldet?

Meist wird der falsche challenge-Wert weitergereicht. Absenden müssen Sie den geetest_challenge aus der API-Antwort, nicht den ursprünglich eingereichten Wert. Zweithäufigste Ursache: ein gekürzter geetest_seccode – der Teil hinter dem Pipe-Zeichen gehört dazu.

Funktioniert derselbe Ablauf auch für GeeTest v4?

Nein. GeeTest v4 nutzt ein anderes Protokoll und wird derzeit nicht unterstützt; der Typ ist als „bald verfügbar“ angekündigt. Für v4-Abfragen ist dieses Skript daher kein gültiger Weg.

Wie viele Threads brauche ich für 5.000 GeeTest-Abfragen pro Tag?

Rechnerisch etwa einen: 5.000 Abfragen × 12 Sekunden ergeben rund 17 Stunden Thread-Zeit auf 24 Stunden verteilt. Praktisch planen Sie Reserve für Lastspitzen ein – STANDARD (30 $/Monat, 15 Threads) fängt ungleichmäßige Verteilungen ab.

Lässt sich GeeTest v3 ohne Headless-Browser lösen?

Ja, sofern Sie gt und challenge per HTTP-Anfrage erreichen. Genau dafür ist Weg 3 gedacht. Ein Browser wird erst nötig, wenn die Parameter ausschließlich nach JavaScript-Ausführung im DOM stehen.

Wie protokolliere ich Fehlversuche in der CI sinnvoll?

Vier Felder reichen, um Timeouts von echten Parameterfehlern zu trennen:

  • Task-ID aus der Antwort von in.php
  • Fehlercode oder letzter Status aus res.php
  • Zeitstempel der Übermittlung
  • Anzahl der Abfragen bis zum Abbruch

Der API-Schlüssel gehört nie ins Log, auch nicht gekürzt.

Weiterführende Artikel

Kommentare sind für diesen Artikel deaktiviert.