GeeTest

GeeTest CAPTCHA Guide — v3, v4, and How to Solve Them

GeeTest is a Chinese CAPTCHA provider whose slide puzzle is deployed on thousands of global sites — particularly in fintech, gaming, and e-commerce. GeeTest challenges are significantly more complex than standard text CAPTCHAs and require specialized solver support. This guide covers both GeeTest v3 and v4, including how they work, which solvers handle them, and working Python integration code.

What Is GeeTest?

GeeTest (极验) is a behavioral CAPTCHA system originating from Wuhan, China. It is one of the most widely deployed CAPTCHAs in Asia-Pacific and appears globally on sites using Chinese-origin infrastructure or security stacks.

GeeTest challenges are interactive: - v3 — Classic slide puzzle ("drag the slider to fit the puzzle piece"), click challenge ("click in order"), or icon challenge ("select the icons in the correct sequence") - v4 — Updated architecture with more challenge variants, improved behavioral analysis, and a different server-side validation flow

Where you'll encounter GeeTest: - Binance, OKX, and other crypto exchanges - Tencent, ByteDance, and related apps - Many Chinese-origin gaming and e-commerce sites - Authentication flows on sites using OWASP-aligned WAFs with GeeTest integration

GeeTest v3 — How It Works

Technical Flow

  1. The page loads GeeTest's JS (gt.js) and calls initGeetest() with a gt key (the static product key for the integration) and a challenge (a dynamic, single-use token fetched from the site's own server at page load).
  2. The user completes the interactive challenge.
  3. GeeTest JS returns three values: geetest_challenge, geetest_validate, and geetest_seccode.
  4. These three values are submitted to the site's server, which verifies them with GeeTest's API.

Critical distinction: The challenge value is dynamic — it changes on every page load and fetched from the site's backend. The solver API requires both the static gt key and the current challenge value to solve.

Extracting v3 Parameters

import re
import requests

def get_geetest_v3_params(page_url: str) -> dict:
    """
    Extract GeeTest v3 gt key and challenge from the page.
    Note: challenge is often fetched dynamically via XHR — may need
    to intercept the network request instead.
    """
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
    html = requests.get(page_url, headers=headers).text

    gt = re.search(r'"gt"\s*:\s*"([0-9a-f]{32})"', html)
    challenge = re.search(r'"challenge"\s*:\s*"([0-9a-f]{32}(?:\.[0-9a-f]+)?)"', html)

    if not gt or not challenge:
        raise ValueError(
            "GeeTest v3 parameters not found in HTML. "
            "The challenge may be fetched via XHR — intercept /captcha/register or similar."
        )
    return {"gt": gt.group(1), "challenge": challenge.group(1)}

For sites that fetch the challenge via XHR, use Playwright to intercept the network request:

from playwright.sync_api import sync_playwright

def get_geetest_v3_via_xhr(page_url: str) -> dict:
    """Intercept the GeeTest challenge XHR to capture gt and challenge."""
    captured = {}

    def handle_response(response):
        if "captcha/register" in response.url or "gt=" in response.url:
            try:
                data = response.json()
                if "gt" in data:
                    captured["gt"] = data["gt"]
                    captured["challenge"] = data["challenge"]
            except Exception:
                pass

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.on("response", handle_response)
        page.goto(page_url, wait_until="networkidle")
        browser.close()

    if not captured:
        raise ValueError("Could not capture GeeTest v3 challenge via XHR")
    return captured

GeeTest v4 — How It Works

GeeTest v4 (released ~2021) uses a different architecture:

  • No separate gt + challenge pair. Instead, it uses a captcha_id (static) and initializes via a POST to GeeTest's API with the session context.
  • The output fields are different: captcha_id, lot_number, pass_token, gen_time, captcha_output.
  • Behavioral scoring is more aggressive — faster solves are penalized more heavily.

v4 is identifiable by new Gt4({captchaId: "..."}) or initGeetest4() in the page JavaScript.

Solver Support for GeeTest

Solver v3 Support v4 Support Success Rate Avg Solve Time Notes
CaptchaAI ~94–96% 10–20s Best combined v3+v4 accuracy per benchmark
2Captcha ~89–93% 15–30s Established; both versions supported
Anti-Captcha ~88–93% 15–25s Reliable at scale
CapSolver ~92–95% 12–22s Strong automation-grade speed
CapMonster Cloud ~86–91% 15–25s Affordable; v4 support added recently
NopeCHA N/A N/A No GeeTest support
DeathByCaptcha ~80–86% 25–40s v3 only; not recommended at volume

GeeTest success rates are more sensitive to IP reputation than reCAPTCHA or Turnstile. Residential proxies improve v3 and v4 solve rates significantly.

How to Solve GeeTest v3 in Python

import requests
import time

def solve_geetest_v3(
    api_key: str,
    page_url: str,
    gt: str,
    challenge: str,
) -> dict:
    """
    Solve GeeTest v3 using CaptchaAI (2Captcha-compatible API).
    Returns dict with geetest_challenge, geetest_validate, geetest_seccode.
    """
    payload = {
        "key": api_key,
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": page_url,
        "json": 1,
    }

    r = requests.post("https://ocr.captchaai.com/in.php", data=payload, timeout=30)
    r.raise_for_status()
    result = r.json()
    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result}")
    task_id = result["request"]

    time.sleep(10)
    for _ in range(24):
        r = requests.get(
            "https://ocr.captchaai.com/res.php",
            params={"key": api_key, "action": "get", "id": task_id, "json": 1},
            timeout=30,
        )
        data = r.json()
        if data.get("status") == 1:
            # Returns pipe-delimited: challenge|validate|seccode
            parts = data["request"].split("|")
            return {
                "geetest_challenge": parts[0],
                "geetest_validate": parts[1],
                "geetest_seccode": parts[2],
            }
        time.sleep(5)

    raise TimeoutError("GeeTest v3 solve timed out")

Submitting the v3 Solution

import json

def submit_geetest_v3_solution(
    form_endpoint: str,
    solution: dict,
    other_fields: dict,
) -> requests.Response:
    """Submit form with GeeTest v3 solution."""
    payload = {**other_fields, **solution}
    r = requests.post(form_endpoint, json=payload, timeout=30)
    return r

How to Solve GeeTest v4 in Python

def solve_geetest_v4(
    api_key: str,
    page_url: str,
    captcha_id: str,
) -> dict:
    """
    Solve GeeTest v4 using CaptchaAI.
    Returns dict with captcha_id, lot_number, pass_token, gen_time, captcha_output.
    """
    payload = {
        "key": api_key,
        "method": "geetest4",
        "captcha_id": captcha_id,
        "pageurl": page_url,
        "json": 1,
    }

    r = requests.post("https://ocr.captchaai.com/in.php", data=payload, timeout=30)
    r.raise_for_status()
    result = r.json()
    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result}")
    task_id = result["request"]

    time.sleep(10)
    for _ in range(30):
        r = requests.get(
            "https://ocr.captchaai.com/res.php",
            params={"key": api_key, "action": "get", "id": task_id, "json": 1},
            timeout=30,
        )
        data = r.json()
        if data.get("status") == 1:
            # v4 returns JSON string
            return json.loads(data["request"])
        time.sleep(5)

    raise TimeoutError("GeeTest v4 solve timed out")

Common Errors and Fixes

ERROR_CAPTCHA_UNSOLVABLE on v3 The challenge value was stale when submitted to the solver. The challenge parameter must be the current session's challenge — it is valid only once and only for the current page load. Re-fetch it immediately before calling the solver.

"Invalid challenge" on server submission (v3) The geetest_challenge in the solution must match the original challenge. If the solver returns a different challenge value in its solution (some implementations do), use the solution's geetest_challenge, not the original.

v4 "lot_number invalid" The captcha_id was incorrect. Inspect the initGeetest4() call or the network request to /api/v1/register to find the correct static ID.

Low success rate (< 85%) on v3 GeeTest v3 performs behavioral analysis on the solve pattern. Flagged datacenter IPs are assigned harder challenges. Use a residential proxy for the page session (not the solver API call).

"Server verification failed" despite valid-looking solution GeeTest v3 server verification is time-sensitive. The site's backend must verify the solution within a few seconds of receiving it. Avoid buffering the solution before submission.

GeeTest v3 vs v4 — Key Differences

Dimension GeeTest v3 GeeTest v4
Init parameters gt (static) + challenge (dynamic) captcha_id (static)
Output fields challenge, validate, seccode lot_number, pass_token, gen_time, captcha_output
Challenge fetch Page HTML or XHR API POST to GeeTest
Behavioral scoring Moderate Aggressive
Solver method param geetest geetest4

When to Use Which Solver

Scenario Recommended Solver Reason
GeeTest v3 or v4, general automation CaptchaAI Best combined accuracy per CaptchaRank benchmark
High-volume GeeTest v3 2Captcha or CapSolver Deep worker pools at scale
Cost-sensitive v3 CapMonster Cloud Competitive pricing
v4 only CapSolver Strong v4-specific implementation
Legacy pipeline 2Captcha No integration changes needed

Explore More in This Hub


Benchmark data sourced from CaptchaRank live performance monitoring. Success rates reflect current data and are updated as solver performance changes.

Comments are disabled for this article.

Related Posts

FunCaptcha / Arkose Labs FunCaptcha (Arkose Labs) — Complete Solving Guide
Everything developers need to know about Fun Captcha (Arkose Labs): how it works, which solvers support it, working Python code, and a decision guide for choosi...

Everything developers need to know about Fun Captcha (Arkose Labs): how it works, which solvers support it, wo...

May 03, 2026
hCaptcha hCaptcha Guide — How It Works and How to Solve It
Complete guide to h Captcha for developers — how it works, which solvers support it, working Python code, and a ranked solver comparison table.

Complete guide to h Captcha for developers — how it works, which solvers support it, working Python code, and...

May 03, 2026