GeeTest v3 presents interactive challenges — slide puzzles, click sequences, or gap-fill tasks. Unlike token-based CAPTCHAs, GeeTest v3 returns three values (challenge, validate, seccode) that must be submitted together to the target site. CaptchaAI handles the visual solving and returns all three values.
This guide covers the full PHP implementation — extracting GeeTest parameters, submitting to CaptchaAI, and injecting the solution.
What you need
| Requirement | Details |
|---|---|
| CaptchaAI API key | captchaai.com |
| PHP 7.4+ | With curl extension |
| gt | GeeTest public site key (static) |
| challenge | Dynamic token loaded with the CAPTCHA (must be fresh) |
| Page URL | Full URL where GeeTest appears |
| api_server (optional) | GeeTest API server (e.g., api-na.geetest.com) |
Step 1: Extract GeeTest parameters
Find the GeeTest parameters in the page source, typically in the initGeetest call:
// Look for this pattern in the page JavaScript:
initGeetest({
gt: "f1ab2cdjja3456116012345b6c78d99e",
challenge: "12345678abc90123d45678ef90123a456b",
product: "bind",
api_server: "api-na.geetest.com"
}, function(captchaObj) { ... });
Critical: The challenge value expires quickly. Always fetch a fresh challenge immediately before solving. Many sites expose a challenge endpoint (e.g., /api/geetest/register) that returns a fresh challenge and gt.
Step 2: Submit the task to CaptchaAI
<?php
$apiKey = "YOUR_API_KEY";
$gt = "f1ab2cdjja3456116012345b6c78d99e";
$challenge = "12345678abc90123d45678ef90123a456b"; // must be fresh
$apiServer = "api-na.geetest.com";
$pageUrl = "https://example.com/login";
// Submit GeeTest task
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://ocr.captchaai.com/in.php?" . http_build_query([
'key' => $apiKey,
'method' => 'geetest',
'gt' => $gt,
'challenge' => $challenge,
'api_server' => $apiServer,
'pageurl' => $pageUrl,
'json' => 1
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if ($data['status'] !== 1) {
die("Submit failed: " . $data['request']);
}
$taskId = $data['request'];
echo "Task submitted. ID: {$taskId}\n";
Step 3: Poll for the result
Wait 15 seconds, then poll every 5 seconds. The result contains three values:
// Poll for result
sleep(15);
$solution = null;
for ($i = 0; $i < 30; $i++) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://ocr.captchaai.com/res.php?" . http_build_query([
'key' => $apiKey,
'action' => 'get',
'id' => $taskId,
'json' => 1
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
if ($result['status'] === 1) {
$solution = json_decode($result['request'], true);
echo "Challenge: " . $solution['challenge'] . "\n";
echo "Validate: " . $solution['validate'] . "\n";
echo "Seccode: " . $solution['seccode'] . "\n";
break;
}
if ($result['request'] !== 'CAPCHA_NOT_READY') {
die("Solve failed: " . $result['request']);
}
echo "Attempt " . ($i + 1) . ": not ready, waiting 5s...\n";
sleep(5);
}
if ($solution === null) {
die("Solve timed out");
}
Step 4: Submit the solution
Send all three values to the target site's verification endpoint:
// Submit GeeTest solution to target site
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://example.com/api/login");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'username' => 'user',
'password' => 'pass',
'geetest_challenge' => $solution['challenge'],
'geetest_validate' => $solution['validate'],
'geetest_seccode' => $solution['seccode']
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "Response status: {$httpCode}\n";
Complete working script
<?php
$apiKey = "YOUR_API_KEY";
$gt = "f1ab2cdjja3456116012345b6c78d99e";
$challenge = "12345678abc90123d45678ef90123a456b";
$apiServer = "api-na.geetest.com";
$pageUrl = "https://example.com/login";
function solveGeetestV3($apiKey, $gt, $challenge, $apiServer, $pageUrl) {
// Submit task
$ch = curl_init("https://ocr.captchaai.com/in.php?" . http_build_query([
'key' => $apiKey,
'method' => 'geetest',
'gt' => $gt,
'challenge' => $challenge,
'api_server' => $apiServer,
'pageurl' => $pageUrl,
'json' => 1
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($data['status'] !== 1) {
throw new Exception("Submit error: " . $data['request']);
}
$taskId = $data['request'];
echo "Task ID: {$taskId}\n";
// Poll for result
sleep(15);
for ($i = 0; $i < 30; $i++) {
$ch = curl_init("https://ocr.captchaai.com/res.php?" . http_build_query([
'key' => $apiKey, 'action' => 'get', 'id' => $taskId, 'json' => 1
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($result['status'] === 1) {
return json_decode($result['request'], true);
}
if ($result['request'] !== 'CAPCHA_NOT_READY') {
throw new Exception("Solve error: " . $result['request']);
}
sleep(5);
}
throw new Exception("Solve timed out");
}
$solution = solveGeetestV3($apiKey, $gt, $challenge, $apiServer, $pageUrl);
echo "Challenge: " . $solution['challenge'] . "\n";
echo "Validate: " . $solution['validate'] . "\n";
echo "Seccode: " . $solution['seccode'] . "\n";
Expected output:
Task ID: 73849562810
Challenge: 12345678abc90123d45678ef90123a456b
Validate: a1b2c3d4e5_validate
Seccode: a1b2c3d4e5_seccode|jordan
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
ERROR_BAD_PARAMETERS |
Missing gt or challenge |
Include both required parameters |
ERROR_CAPTCHA_UNSOLVABLE |
Expired challenge token | Get a fresh challenge immediately before submitting |
ERROR_WRONG_USER_KEY |
Invalid API key | Check key is 32 characters |
ERROR_ZERO_BALANCE |
Insufficient balance | Top up your account |
| Solution rejected by site | Stale challenge | The challenge must be used within seconds of generation |
| Solution rejected by site | Wrong field names | Check the site's expected parameter names (may vary) |
FAQ
Why does my challenge keep expiring?
GeeTest challenge tokens have short lifespans. Fetch a fresh challenge from the site's registration endpoint immediately before submitting to CaptchaAI.
What are the three return values?
challenge is the original challenge token (may be modified), validate is the validation hash, and seccode is the security code. All three must be submitted together.
How do I find the api_server?
Look for api_server in the initGeetest configuration. Common values include api-na.geetest.com and api.geetest.com. If not specified, omit it from your request.
Does GeeTest v3 require a proxy?
Not required, but a proxy can improve success rates on sites that check IP consistency.
What is the typical solve time?
15–25 seconds for GeeTest v3 challenges.
Start solving GeeTest v3
Get your API key at captchaai.com. Use method=geetest with a fresh challenge token.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.