Most GeeTest v3 failures fall into one of three buckets: request-stage mistakes (submitting to the API), result-stage mistakes (polling for the answer), and target-page validation failures (the API returns values but the page still rejects them). The single biggest GeeTest-specific issue is almost always the same: a stale challenge value.
CaptchaAI's GeeTest v3 API docs are explicit: you must get a new challenge value for each solve request. Once the captcha loads on the page, the old challenge becomes invalid. That means a GeeTest integration can fail even when the request looks mostly correct.
This guide walks through every common failure pattern and the fastest fix for each one.
The #1 GeeTest failure: stale challenge
If there is one thing to check first, it is challenge freshness.
GeeTest v3 requires two key parameters:
gt— the public website key (static, does not change)challenge— the dynamic challenge key (changes on every page load)
Why it breaks
The challenge value is generated when the GeeTest widget initializes on the page. If you capture it once and reuse it across multiple solve requests, every request after the first one will either:
- be rejected by the API at submission time, or
- produce a result the target page rejects because the challenge has expired
How to fix it
Before every solve request, inspect the page's network requests to find the API call that returns a fresh challenge. Replay that request to get a new value, then immediately submit it to CaptchaAI.
# Pseudocode: fetch a fresh challenge before each solve
import requests
def get_fresh_challenge(target_url):
"""Hit the GeeTest init endpoint to get a new challenge."""
resp = requests.get(f"{target_url}/geetest/register", timeout=10)
data = resp.json()
return data["challenge"], data["gt"]
challenge, gt = get_fresh_challenge("https://example.com")
# Now submit to CaptchaAI immediately — do not delay
Rule of thumb: If the time between capturing the
challengeand submitting the solve request is more than a few seconds, refresh it.
Request-stage errors
These failures happen when you submit the task to https://ocr.captchaai.com/in.php.
ERROR_WRONG_USER_KEY
Cause: The API key format is incorrect (it should be 32 characters).
Fix: Verify the key from captchaai.com/api.php. Do not add extra characters or whitespace.
ERROR_KEY_DOES_NOT_EXIST
Cause: The API key is correctly formatted but does not match any active account.
Fix: Log in to your CaptchaAI dashboard and confirm your key is active.
ERROR_ZERO_BALANCE
Cause: No free threads available on your current plan.
Fix: Wait for threads to free up, reduce concurrency, or upgrade your plan.
ERROR_PAGEURL
Cause: The pageurl parameter is missing from the request.
Fix: Add the full URL of the page where the GeeTest widget loads. Example:
pageurl=https://example.com/login
ERROR_BAD_PARAMETERS
Cause: One or more required fields are missing or malformed. For GeeTest, the required parameters are:
| Parameter | Type | Required | Description |
|---|---|---|---|
key |
String | Yes | Your CaptchaAI API key |
method |
String | Yes | Must be geetest |
gt |
String | Yes | Static public website key |
challenge |
String | Yes | Dynamic challenge key (must be fresh) |
pageurl |
String | Yes | Full page URL |
Fix: Check that gt, challenge, and pageurl are all present and correctly formatted.
HTML or 500/502 responses
Cause: Transient server-side error — not a parameter problem.
Fix: Wait 5–10 seconds and retry the request.
Result-stage errors
These failures happen when you poll https://ocr.captchaai.com/res.php.
CAPCHA_NOT_READY
This is not an error. It means the captcha is still being solved. GeeTest v3 solves at CaptchaAI typically take under 12 seconds with a 100% success rate.
Fix: Wait 5 seconds and poll again. Do not treat this as a failure.
ERROR_WRONG_ID_FORMAT
Cause: The captcha ID format is wrong — IDs should be numeric only.
Fix: Verify you are using the exact ID returned by in.php, without modification.
ERROR_WRONG_CAPTCHA_ID
Cause: The ID does not match any submitted task.
Fix: Check that you are using the correct ID from the submission response. If you submitted multiple tasks, make sure you are polling the right one.
ERROR_EMPTY_ACTION
Cause: The action parameter is missing or empty in your poll request.
Fix: Include action=get in every poll request:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID
ERROR_CAPTCHA_UNSOLVABLE
Cause: The challenge could not be solved — possibly due to a stale challenge value or an unsupported GeeTest variant.
Fix: Refresh the challenge value and retry.
ERROR_INTERNAL_SERVER_ERROR
Cause: Server-side issue at CaptchaAI.
Fix: Wait 10 seconds and retry.
Target-page validation failures
These are the hardest failures to debug because the CaptchaAI API returns a valid result, but the target page still rejects it.
When a GeeTest v3 solve succeeds, the API returns three values:
{
"challenge": "1a2b3456cd67890e12345fab678901c2de",
"validate": "09fe8d7c6ba54f32e1dcb0a9fedc8765",
"seccode": "12fe3d4c56789ba01f2e345d6789c012|jordan"
}
These must be submitted to the target page as:
| API response field | Target page field |
|---|---|
challenge |
geetest_challenge |
validate |
geetest_validate |
seccode |
geetest_seccode |
Failure 1: Wrong field mapping
Symptom: API returns values, but the target page rejects them immediately.
Cause: The returned values are inserted into the wrong fields or the wrong request path.
Fix: Inspect the network traffic from a manual GeeTest solve on the target page. Find the POST request that submits the GeeTest result and match your field names exactly.
Failure 2: Stale challenge used upstream
Symptom: The API returns values, but the page says the challenge is expired or invalid.
Cause: The challenge value was captured too early or reused.
Fix: Fetch a fresh challenge immediately before each solve request. Do not cache or reuse it.
Failure 3: Wrong page context
Symptom: Validation fails even with fresh inputs.
Cause: The pageurl sent to CaptchaAI does not match the actual page where the GeeTest widget was loaded.
Fix: Use the exact URL, including protocol and path. If the widget is loaded via AJAX on a different route, use that route's URL.
Failure 4: Request-structure mismatch
Symptom: Fields are correct but the request format is wrong.
Cause: The target page expects the GeeTest fields in a specific content type (e.g., JSON body vs. form-encoded) or alongside other form fields.
Fix: Compare your submission request to the network traffic from a manual solve. Match the content type, field order, and any additional fields.
Quick error-to-fix reference
| Error / Symptom | Stage | Likely cause | Fix |
|---|---|---|---|
ERROR_WRONG_USER_KEY |
Submit | Malformed API key | Verify 32-char key |
ERROR_KEY_DOES_NOT_EXIST |
Submit | Invalid key | Check dashboard |
ERROR_ZERO_BALANCE |
Submit | No free threads | Wait or upgrade plan |
ERROR_PAGEURL |
Submit | Missing pageurl |
Add full page URL |
ERROR_BAD_PARAMETERS |
Submit | Missing gt, challenge, or pageurl |
Verify all required fields |
CAPCHA_NOT_READY |
Poll | Solve in progress | Wait 5 seconds, retry |
ERROR_WRONG_ID_FORMAT |
Poll | Non-numeric captcha ID | Use exact ID from in.php |
ERROR_WRONG_CAPTCHA_ID |
Poll | Invalid captcha ID | Verify submission ID |
ERROR_EMPTY_ACTION |
Poll | Missing action=get |
Add action parameter |
ERROR_CAPTCHA_UNSOLVABLE |
Poll | Stale challenge or unsupported variant | Refresh challenge, retry |
| API returns values but page rejects | Validation | Stale challenge, wrong fields, wrong URL | Refresh challenge, verify field mapping |
Python: complete GeeTest v3 solve with fresh challenge
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def get_fresh_challenge(target_url):
"""Fetch a fresh GeeTest challenge from the target page."""
resp = requests.get(f"{target_url}/api/geetest/register", timeout=10)
data = resp.json()
return data["gt"], data["challenge"]
def solve_geetest_v3(api_key, gt, challenge, pageurl):
"""Submit a GeeTest v3 challenge and return the validation package."""
# Submit
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "geetest",
"gt": gt,
"challenge": challenge,
"pageurl": pageurl,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Wait before first poll
time.sleep(15)
# Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return result_data["request"]
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("GeeTest v3 solve timed out")
# Usage: always fetch a fresh challenge first
PAGE_URL = "https://example.com/login"
gt, challenge = get_fresh_challenge(PAGE_URL)
result = solve_geetest_v3(API_KEY, gt, challenge, PAGE_URL)
print(f"Result: {result}")
# The result contains: challenge, validate, seccode
# Map them to: geetest_challenge, geetest_validate, geetest_seccode
Node.js: complete GeeTest v3 solve with fresh challenge
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getFreshChallenge(targetUrl) {
const resp = await fetch(`${targetUrl}/api/geetest/register`);
const data = await resp.json();
return { gt: data.gt, challenge: data.challenge };
}
async function solveGeetestV3(apiKey, gt, challenge, pageurl) {
// Submit
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "geetest",
gt: gt,
challenge: challenge,
pageurl: pageurl,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
await sleep(15_000);
// Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return resultData.request;
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("GeeTest v3 solve timed out");
}
// Usage
const PAGE_URL = "https://example.com/login";
(async () => {
const { gt, challenge } = await getFreshChallenge(PAGE_URL);
const result = await solveGeetestV3(API_KEY, gt, challenge, PAGE_URL);
console.log("Result:", result);
// Map result fields to: geetest_challenge, geetest_validate, geetest_seccode
})();
FAQ
Why does GeeTest keep failing even when the request looks correct?
The most common cause is a stale challenge value. Even if gt and pageurl are correct, an expired challenge will cause the API to fail or the target page to reject the returned values. Always fetch a fresh challenge immediately before each solve request.
What is the most common GeeTest v3 mistake?
Reusing a challenge value that is no longer fresh. The CaptchaAI docs explicitly warn that once the captcha loads on the page, the old challenge becomes invalid.
What does CAPCHA_NOT_READY mean?
It means the solve is still in progress — not an error. Wait 5 seconds and poll res.php again. GeeTest v3 solves at CaptchaAI typically take under 12 seconds.
What should I do if the API returns values but the page still rejects them?
Check three things in order:
- Challenge freshness — Was the
challengefetched immediately before submission? - Field mapping — Are
geetest_challenge,geetest_validate, andgeetest_seccodemapped correctly to the target page's expected fields? - Request structure — Does the target page expect JSON, form-encoded data, or another format? Compare to a manual solve's network traffic.
How is GeeTest v3 different from reCAPTCHA v2?
GeeTest v3 is a puzzle/slider challenge (not a checkbox). It requires a dynamic challenge parameter that must be refreshed for each solve. The API returns three validation fields (challenge, validate, seccode) instead of a single token. For reCAPTCHA v2 solving, see How to Solve reCAPTCHA v2 Using API.
Does CaptchaAI support GeeTest v4?
This article covers GeeTest v3 only. Check the CaptchaAI API docs for the latest supported captcha types.
Fix your GeeTest workflow
If your GeeTest integration is failing:
- Check the challenge — Is it fresh? Fetch a new one immediately before each solve.
- Verify parameters —
gt,challenge,pageurlmust all be correct. - Inspect field mapping — The returned
challenge,validate, andseccodemust go into the right fields. - Compare to a manual solve — Use browser DevTools to capture the exact request structure from a successful manual GeeTest solve.
Start with the CaptchaAI GeeTest v3 solver, confirm your parameters against the API docs, and read How GeeTest v3 Captcha Works if you need background on the challenge flow.
Iteration log
| Iteration | Focus | Changes |
|---|---|---|
| Draft 1 | Structure and content | Initial troubleshooting draft — 3 error stages, error-to-fix table, FAQ |
| Draft 2 | Technical accuracy | Verified all error codes and GeeTest parameters against captchaai.com/api-docs. Added API parameter table. Confirmed challenge/validate/seccode field mapping. |
| Draft 3 | Code examples | Added complete Python and Node.js examples with fresh-challenge fetching. Added pseudocode for challenge refresh pattern. |
| Draft 4 | Validation failures depth | Expanded target-page validation section with 4 distinct failure modes. Added field mapping table. Added request-structure mismatch diagnosis. |
| Draft 5 | Final QA polish | Verified all error codes match official docs. Added quick-reference table. Tightened intro. Added cross-links to cluster articles. Confirmed FAQ answers are schema-ready. |
Visual asset brief
Hero image
- Alt text: Developer troubleshooting GeeTest v3 errors — request, polling, and validation failure diagnosis
- Must show: Debugging context with error flow stages and failure points
- File name: geetest-v3-errors-troubleshooting-hero.png
In-article visual 1
- Placement: After "Result-stage errors"
- Type: Decision tree
- Alt text: Decision tree for GeeTest v3 failures — request errors vs polling errors vs validation failures
- File name: geetest-v3-error-decision-tree.png
In-article visual 2
- Placement: After "Target-page validation failures"
- Type: Causes-and-fixes diagram
- Alt text: Diagram showing common causes of GeeTest v3 page rejection and their fixes
- File name: geetest-v3-validation-causes-fixes.png
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.