GeeTest v3 is a slider-based CAPTCHA commonly found on Chinese and international platforms. Unlike reCAPTCHA, GeeTest requires extracting specific challenge parameters (gt, challenge) before solving. This guide shows the complete flow using Python and CaptchaAI's GeeTest solver — which achieves 100% success rate.
Prerequisites
pip install requests
You need:
- A CaptchaAI API key from captchaai.com
- The target page URL
- GeeTest
gt(account ID) andchallenge(per-session token)
GeeTest v3 parameters
GeeTest v3 requires two parameters for solving:
| Parameter | Description | Where to find |
|---|---|---|
gt |
GeeTest account ID (32-char hex) | Static in page source or API response |
challenge |
Per-session challenge token (32-char hex) | Dynamic — must be fetched fresh per solve |
How parameters flow
1. Page loads → JavaScript requests GeeTest API
2. API returns: { gt: "abc...", challenge: "def...", success: 1 }
3. User completes slider
4. GeeTest returns: { geetest_challenge, geetest_validate, geetest_seccode }
5. Server verifies with GeeTest backend
Step 1: Extract GeeTest parameters
Method A: From page HTML
import re
import requests
def extract_geetest_from_html(url):
"""Extract GeeTest gt and challenge from page HTML."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
}
response = requests.get(url, headers=headers, timeout=15)
html = response.text
# Extract gt (account ID)
gt_match = re.search(r'gt\s*[:=]\s*["\']([a-f0-9]{32})["\']', html)
gt = gt_match.group(1) if gt_match else None
# Extract challenge
challenge_match = re.search(
r'challenge\s*[:=]\s*["\']([a-f0-9]{32})["\']', html
)
challenge = challenge_match.group(1) if challenge_match else None
return {"gt": gt, "challenge": challenge}
Method B: From GeeTest register API (more common)
def extract_geetest_from_api(register_url):
"""Fetch fresh GeeTest parameters from the site's register endpoint."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Referer": "https://example.com/login",
}
response = requests.get(register_url, headers=headers, timeout=15)
data = response.json()
# Common response format:
# { "gt": "abc123...", "challenge": "def456...", "success": 1 }
return {
"gt": data.get("gt"),
"challenge": data.get("challenge"),
"success": data.get("success"),
}
# Example: Many sites have a /geetest/register endpoint
params = extract_geetest_from_api("https://example.com/api/geetest/register")
print(f"GT: {params['gt']}")
print(f"Challenge: {params['challenge']}")
Method C: Intercept from network requests
from selenium import webdriver
from selenium.webdriver.common.by import By
import json
def extract_geetest_from_network(url):
"""Use Selenium to intercept GeeTest register call."""
options = webdriver.ChromeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
options.set_capability("goog:loggingPrefs", {"performance": "ALL"})
driver = webdriver.Chrome(options=options)
driver.get(url)
import time
time.sleep(5) # Wait for GeeTest to initialize
# Check performance logs for GeeTest API calls
logs = driver.get_log("performance")
for entry in logs:
message = json.loads(entry["message"])["message"]
if message["method"] == "Network.responseReceived":
resp_url = message["params"]["response"]["url"]
if "geetest" in resp_url or "gt=" in resp_url:
# Found the GeeTest register call
request_id = message["params"]["requestId"]
body = driver.execute_cdp_cmd(
"Network.getResponseBody", {"requestId": request_id}
)
data = json.loads(body["body"])
driver.quit()
return {"gt": data.get("gt"), "challenge": data.get("challenge")}
driver.quit()
return None
Step 2: Submit to CaptchaAI
import requests
API_KEY = "YOUR_API_KEY"
def submit_geetest(gt, challenge, page_url):
"""Submit GeeTest v3 solving task to CaptchaAI."""
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": page_url,
"json": 1,
})
data = response.json()
if data.get("status") != 1:
raise Exception(f"Submit failed: {data.get('request')}")
return data["request"]
Step 3: Poll for the result
GeeTest returns three values instead of a single token:
import time
def poll_geetest_result(task_id, timeout=120):
"""Poll CaptchaAI for GeeTest v3 solution."""
start = time.time()
while time.time() - start < timeout:
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:
# GeeTest returns three values
answer = result["request"]
# Format: "geetest_challenge:xxx|geetest_validate:xxx|geetest_seccode:xxx"
return parse_geetest_response(answer)
if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
raise Exception("GeeTest could not be solved")
raise TimeoutError("Solve timed out")
def parse_geetest_response(response_str):
"""Parse GeeTest response into individual components."""
parts = {}
for pair in response_str.split("|"):
if ":" in pair:
key, value = pair.split(":", 1)
parts[key] = value
return parts
Complete working example
import re
import time
import requests
API_KEY = "YOUR_API_KEY"
TARGET_URL = "https://example.com/login"
GEETEST_REGISTER_URL = "https://example.com/api/geetest/register"
def get_geetest_params(session, register_url):
"""Fetch fresh GeeTest parameters."""
response = session.get(register_url, timeout=15)
data = response.json()
return data["gt"], data["challenge"]
def solve_geetest(gt, challenge, page_url):
"""Solve GeeTest v3 via CaptchaAI."""
# Submit
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": page_url,
"json": 1,
})
data = submit.json()
if data.get("status") != 1:
raise Exception(f"Submit error: {data.get('request')}")
task_id = data["request"]
print(f"Task submitted: {task_id}")
# Poll
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:
return result["request"]
raise TimeoutError("Solve timed out")
# --- Main flow ---
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "application/json, text/html, */*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
})
# 1. Get fresh challenge parameters
gt, challenge = get_geetest_params(session, GEETEST_REGISTER_URL)
print(f"GT: {gt}")
print(f"Challenge: {challenge}")
# 2. Solve GeeTest
solution = solve_geetest(gt, challenge, TARGET_URL)
print(f"Solution received")
# 3. Submit to target site
# The exact submission format varies by site
form_response = session.post(TARGET_URL, data={
"geetest_challenge": challenge,
"geetest_validate": solution.split("|")[1].split(":")[1] if "|" in solution else solution,
"geetest_seccode": solution.split("|")[2].split(":")[1] if "|" in solution else f"{solution}|jordan",
"username": "user@example.com",
"password": "password123",
})
print(f"Login status: {form_response.status_code}")
GeeTest response values
The CaptchaAI response contains three components that the target site validates:
| Value | Description | Format |
|---|---|---|
geetest_challenge |
The original challenge token | 32-char hex string |
geetest_validate |
Validation hash | 32-char hex string |
geetest_seccode |
Security code | {validate}\|jordan |
Constructing the seccode
The geetest_seccode is typically the geetest_validate value followed by |jordan:
validate = "abc123..." # From CaptchaAI response
seccode = f"{validate}|jordan"
Common GeeTest v3 patterns
Pattern 1: JSON API submission
response = session.post("https://example.com/api/verify", json={
"geetest_challenge": challenge,
"geetest_validate": validate,
"geetest_seccode": seccode,
"action": "login",
})
Pattern 2: Form POST
response = session.post("https://example.com/login", data={
"geetest_challenge": challenge,
"geetest_validate": validate,
"geetest_seccode": seccode,
"email": "user@example.com",
"password": "pass123",
})
Pattern 3: Combined with CSRF token
# Some sites require CSRF token alongside GeeTest
csrf_match = re.search(r'name="csrf_token" value="([^"]+)"', page_html)
csrf_token = csrf_match.group(1) if csrf_match else ""
response = session.post("https://example.com/login", data={
"csrf_token": csrf_token,
"geetest_challenge": challenge,
"geetest_validate": validate,
"geetest_seccode": seccode,
"username": "user",
})
Error handling
class GeeTestSolveError(Exception):
pass
def solve_geetest_robust(gt, challenge, page_url, max_retries=3):
"""Solve GeeTest with robust error handling."""
for attempt in range(1, max_retries + 1):
try:
submit = requests.post(
"https://ocr.captchaai.com/in.php",
data={
"key": API_KEY,
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": page_url,
"json": 1,
},
timeout=30,
)
submit.raise_for_status()
data = submit.json()
if data.get("status") != 1:
error = data.get("request", "Unknown")
if error in ("ERROR_WRONG_USER_KEY", "ERROR_ZERO_BALANCE"):
raise GeeTestSolveError(f"Fatal: {error}")
raise GeeTestSolveError(f"Submit: {error}")
task_id = data["request"]
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,
}, timeout=30).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
break
print(f"Attempt {attempt} failed")
except requests.RequestException as e:
print(f"Network error attempt {attempt}: {e}")
if attempt == max_retries:
raise GeeTestSolveError(f"Network failure: {e}")
time.sleep(10)
raise GeeTestSolveError(f"Failed after {max_retries} attempts")
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| "Invalid gt" error | GT parameter wrong | Verify 32-char hex format |
| Challenge expired | Challenge token is per-session, expires fast | Fetch fresh challenge immediately before solving |
| Solve succeeds but site rejects | Challenge reused or expired | Use fresh challenge per attempt |
| "geetest_validate" empty | Response parsing error | Check CaptchaAI response format |
| GeeTest API returns success=0 | GeeTest in fallback mode | Check if site uses offline challenge |
Frequently asked questions
How fresh does the challenge need to be?
The challenge token expires within 60-120 seconds. Always fetch a fresh challenge immediately before submitting to CaptchaAI. Never cache or reuse challenges.
What's the difference between GeeTest v3 and v4?
GeeTest v3 uses gt + challenge parameters. GeeTest v4 uses captcha_id + lot_number. This guide covers v3 only.
What's the solve time?
GeeTest v3 typically solves in 5-15 seconds. CaptchaAI achieves a 100% success rate on GeeTest v3.
Can I solve GeeTest offline (fallback) mode?
GeeTest fallback mode is used when GeeTest's servers are unreachable. The challenge type changes. Contact CaptchaAI support for fallback mode handling.
Summary
Solving GeeTest v3 with Python requires: fetch fresh gt and challenge parameters from the site's register API, submit to CaptchaAI using method=geetest, then submit the three result values (geetest_challenge, geetest_validate, geetest_seccode) to the target site. Always use fresh challenge tokens — they expire in under 2 minutes.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.