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
>=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.
Related Articles
- Solve Geetest V3 Nodejs
- Solving Geetest V3 Nodejs Captchaai
- Recaptcha Enterprise Assessment Api Deep Dive
Next Steps
Solve GeeTest v3 challenges — get your CaptchaAI API key and integrate the three-value response into your workflow.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.