GeeTest challenges expire quickly. If you extract the challenge parameters, wait too long, and then solve — the target site rejects the token because the challenge has expired.
GeeTest Timing
| Phase | Time Limit |
|---|---|
| Challenge token validity | 60-120 seconds |
| Solve time (CaptchaAI) | 10-30 seconds |
| Token submission window | Must complete within challenge validity |
| Total budget | ~60-90 seconds from extraction to submission |
The Problem
1. Extract gt + challenge from page ← Clock starts
2. Submit to CaptchaAI ← Takes 1-5 seconds
3. Wait for solve ← Takes 10-30 seconds
4. Get token ← Must still be valid
5. Submit to target site ← Must happen fast
If step 2-5 takes longer than the challenge validity, the token is rejected.
Solution: Extract and Solve Immediately
import requests
import time
import re
def extract_geetest_params(page_url, session=None):
"""Extract fresh GeeTest parameters from page."""
s = session or requests.Session()
resp = s.get(page_url, timeout=15)
# Extract gt key
gt_match = re.search(r'"gt":\s*"([^"]+)"', resp.text)
challenge_match = re.search(r'"challenge":\s*"([^"]+)"', resp.text)
if not gt_match or not challenge_match:
# Try API endpoint (many sites fetch challenge via AJAX)
api_match = re.search(r'captcha\?.*', resp.text)
if api_match:
api_resp = s.get(f"{page_url}/{api_match.group()}", timeout=15)
data = api_resp.json()
return data.get("gt"), data.get("challenge")
if gt_match and challenge_match:
return gt_match.group(1), challenge_match.group(1)
return None, None
def solve_geetest_fast(api_key, page_url, session=None):
"""Extract, solve, and return GeeTest token as fast as possible."""
s = session or requests.Session()
# Step 1: Extract fresh parameters
extraction_start = time.time()
gt, challenge = extract_geetest_params(page_url, s)
if not gt or not challenge:
raise RuntimeError("Could not extract GeeTest parameters")
print(f"Extracted params in {time.time() - extraction_start:.1f}s")
# Step 2: Submit to CaptchaAI immediately
resp = 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)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(f"Submit error: {result.get('request')}")
task_id = result["request"]
# Step 3: Poll aggressively
time.sleep(10) # GeeTest typically takes 10-30s
for _ in range(16): # 80s max
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key,
"action": "get",
"id": task_id,
"json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
total_time = time.time() - extraction_start
print(f"Solved in {total_time:.1f}s total")
if total_time > 90:
print("WARNING: Challenge may have expired")
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {data['request']}")
time.sleep(5)
raise TimeoutError("GeeTest solve timeout")
Handling Challenge API Endpoints
Many sites fetch GeeTest challenges via an AJAX call:
def get_fresh_challenge(api_url, session):
"""Fetch a fresh GeeTest challenge from site's API."""
resp = session.get(api_url, timeout=15)
data = resp.json()
return {
"gt": data["gt"],
"challenge": data["challenge"],
"new_captcha": data.get("new_captcha", True),
}
# Common API patterns:
# GET /api/captcha/init
# GET /captcha?t=TIMESTAMP
# GET /geetest/register?t=TIMESTAMP
Retry with Fresh Challenge
def solve_geetest_with_retry(api_key, page_url, challenge_api, max_attempts=3):
"""Retry GeeTest solve with fresh challenge each time."""
session = requests.Session()
for attempt in range(max_attempts):
try:
# Always get FRESH challenge
params = get_fresh_challenge(challenge_api, session)
# Solve
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "geetest",
"gt": params["gt"],
"challenge": params["challenge"],
"pageurl": page_url,
"json": 1,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
print(f"Attempt {attempt + 1}: Submit error")
continue
task_id = result["request"]
# Poll
time.sleep(10)
for _ in range(12):
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
break
time.sleep(5)
except Exception as e:
print(f"Attempt {attempt + 1} failed: {e}")
raise RuntimeError("Max GeeTest solve attempts exceeded")
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Token always rejected | Challenge expired during solve | Extract → solve → submit in < 60s |
| Can't find challenge parameter | Loaded via AJAX | Intercept network requests |
| Challenge changes on each load | Normal GeeTest behavior | Always use fresh challenge |
| Same gt key, different challenge | gt is static, challenge is dynamic | Re-fetch challenge, keep gt |
FAQ
How do I find the challenge API endpoint?
Open browser DevTools → Network tab → look for requests containing "geetest", "captcha", or "register" when the page loads. The response will contain gt and challenge.
Can I cache the gt parameter?
Yes. The gt key is usually static per site. Only the challenge token needs to be fresh. Cache gt and re-fetch challenge before each solve.
What if the site uses GeeTest v4?
GeeTest v4 uses a different API structure. Check CaptchaAI's documentation for v4-specific parameters.
Related Guides
Solve GeeTest fast — get CaptchaAI with 100% accuracy.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.