Explainers

GeeTest v3 Challenge-Response Workflow: Technical Deep Dive

GeeTest v3 uses a multi-step challenge-response protocol. Unlike reCAPTCHA where a single token solves everything, GeeTest involves a registration step, a challenge token exchange, and a final validation — each producing different parameters. Understanding this flow is essential for correct integration with CaptchaAI.

The Two-Phase Protocol

GeeTest v3 operates in two phases:

Phase 1: Registration (Server-Side)

The site's backend contacts GeeTest to register a new challenge:

Site Backend → GeeTest Server: "Give me a challenge for this user"
GeeTest Server → Site Backend: { gt, challenge, new_captcha }
Site Backend → Browser: Passes gt and challenge to the page

Phase 2: Verification (Client-Side + Server-Side)

The browser renders the challenge, the user solves it, and the result is verified:

Browser: Renders slider/puzzle using gt + challenge
User: Solves the challenge
Browser → Site Backend: { geetest_challenge, geetest_validate, geetest_seccode }
Site Backend → GeeTest Server: Verifies the three values
GeeTest Server → Site Backend: { result: "success" }

Detailed Flow

Step 1: Registration API Call

The site's backend calls GeeTest's registration endpoint:

GET https://api.geetest.com/register.php?gt=GT_ID&json_format=1

Response:

{
  "success": 1,
  "gt": "81dc9bdb52d04dc20036dbd8313ed055",
  "challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "new_captcha": true
}
Parameter Meaning
gt GeeTest ID — identifies the site's GeeTest account
challenge Unique challenge token for this session
new_captcha Whether to use the new CAPTCHA format

Important: The challenge value is single-use and time-limited. Each page load generates a new challenge.

Step 2: Challenge Rendering

The browser receives gt and challenge and initializes the GeeTest widget:

initGeetest({
  gt: "81dc9bdb52d04dc20036dbd8313ed055",
  challenge: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  offline: false,
  new_captcha: true,
  product: "float"
}, function(captchaObj) {
  captchaObj.appendTo('#captcha-container');
  captchaObj.onSuccess(function() {
    var result = captchaObj.getValidate();
    // result contains: geetest_challenge, geetest_validate, geetest_seccode
  });
});

Step 3: Challenge Types

GeeTest v3 supports several challenge types:

Type User action Description
Slider Drag puzzle piece Move a puzzle piece to complete the image
Icon click Click icons in order Click specific icons in the shown sequence
Word click Click characters Click Chinese characters in the correct order
Space Click/select Spatial reasoning challenge

The challenge type is determined by GeeTest based on the site's configuration and user risk profile.

Step 4: Solution Values

After solving, the widget produces three values:

{
  "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
  "geetest_validate": "abc123def456_validate",
  "geetest_seccode": "abc123def456_validate|jordan"
}
Value Description
geetest_challenge Modified challenge token (original + 2 extra chars)
geetest_validate Validation hash
geetest_seccode Security code (validate + \|jordan suffix)

Step 5: Server-Side Verification

The site backend sends these three values to GeeTest for verification:

POST https://api.geetest.com/validate.php

seccode=abc123def456_validate|jordan
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy
&sdk=geetest-python-3.0.0

GeeTest responds with:

{
  "seccode": "abc123def456_validate",
  "validate": "abc123def456_validate"
}

Extracting Parameters for CaptchaAI

To solve with CaptchaAI, you need gt and challenge from the page:

Method 1: Intercept the Registration Response

from playwright.sync_api import sync_playwright
import json

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    geetest_params = {}

    def handle_response(response):
        if "register" in response.url and "geetest" in response.url:
            data = response.json()
            geetest_params["gt"] = data.get("gt")
            geetest_params["challenge"] = data.get("challenge")

    page.on("response", handle_response)
    page.goto("https://example.com/login")

    # Wait for GeeTest to load
    page.wait_for_selector(".geetest_holder")
    print(f"gt: {geetest_params.get('gt')}")
    print(f"challenge: {geetest_params.get('challenge')}")

Method 2: Extract from Page JavaScript

gt = page.evaluate("() => document.querySelector('[data-gt]')?.dataset.gt")
challenge = page.evaluate("() => document.querySelector('[data-challenge]')?.dataset.challenge")

Method 3: From initGeetest Call

Search the page source for the initGeetest call:

import re
source = page.content()
gt_match = re.search(r"gt['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)
challenge_match = re.search(r"challenge['\"]?\s*[:=]\s*['\"]([a-f0-9]{32})['\"]", source)

Solving with CaptchaAI

Submit the extracted parameters:

POST https://ocr.captchaai.com/in.php

key=YOUR_API_KEY
&method=geetest
&gt=81dc9bdb52d04dc20036dbd8313ed055
&challenge=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
&pageurl=https://example.com/login
&json=1

Poll for the result:

GET https://ocr.captchaai.com/res.php?key=YOUR_API_KEY&action=get&id=TASK_ID&json=1

CaptchaAI returns:

{
  "status": 1,
  "request": {
    "geetest_challenge": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6xy",
    "geetest_validate": "abc123def456_validate",
    "geetest_seccode": "abc123def456_validate|jordan"
  }
}

You receive all three values needed for the site's verification step.

Offline vs Online Mode

GeeTest v3 has a fallback mode when GeeTest servers are unreachable:

Mode success value Behavior
Online 1 Normal challenge-response with GeeTest servers
Offline 0 Simplified local verification

In offline mode, the challenge is generated locally and verification is simpler. Most sites use online mode.

Troubleshooting

Issue Cause Fix
challenge value is empty Registration failed Check if the site loads GeeTest correctly
Solution rejected Challenge expired Extract fresh challenge and solve immediately
Wrong gt value Multiple GeeTest instances on page Extract gt from the correct widget
Three values returned but form doesn't submit Missing form fields Inject all three values into the correct inputs

FAQ

Why does GeeTest need both gt and challenge?

The gt identifies the site's GeeTest account (persistent). The challenge is a session-specific token that prevents replay attacks — each challenge can only be solved once.

Can I reuse a GeeTest challenge?

No. Each challenge value is single-use. After it's solved (or expires), you must get a new one from the registration API.

How long does a GeeTest challenge last?

Challenges typically expire within 1-2 minutes. Solve and submit promptly after extracting the parameters.

Next Steps

Solve GeeTest v3 challenges — get your CaptchaAI API key and integrate the three-value response into your workflow.

Discussions (0)

No comments yet.