Tutorials

Solving GeeTest v3 with Node.js and CaptchaAI API

GeeTest v3 is a slide-puzzle CAPTCHA common on Chinese websites and global platforms. Solving it requires extracting two parameters — gt and challenge — and submitting three response values. This guide covers the complete Node.js flow.


GeeTest v3 parameters

Parameter Description Where to find
gt Public key (static per site) HTML source or API response
challenge One-time challenge token API response (changes per load)

The solve returns three values:

Response field Purpose
geetest_challenge Verified challenge string
geetest_validate Validation hash
geetest_seccode Security code (validate + |jordan)

Step 1: Extract gt and challenge

Method A: From HTML source

async function extractGeeTestFromHTML(url) {
  const resp = await fetch(url, {
    headers: {
      "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    },
  });
  const html = await resp.text();

  const gtMatch = html.match(/gt\s*[:=]\s*["']([a-f0-9]{32})["']/);
  const challengeMatch = html.match(
    /challenge\s*[:=]\s*["']([a-f0-9]{32})["']/
  );

  if (!gtMatch) throw new Error("GeeTest gt not found in HTML");
  if (!challengeMatch) throw new Error("GeeTest challenge not found in HTML");

  return {
    gt: gtMatch[1],
    challenge: challengeMatch[1],
  };
}

Method B: From API endpoint

Many sites load GeeTest parameters from an API:

async function extractGeeTestFromAPI(apiUrl) {
  const resp = await fetch(apiUrl, {
    headers: {
      "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    },
  });
  const data = await resp.json();

  // Common response formats
  const gt = data.gt || data.data?.gt || data.captcha?.gt;
  const challenge =
    data.challenge || data.data?.challenge || data.captcha?.challenge;

  if (!gt || !challenge) {
    throw new Error("GeeTest parameters not found in API response");
  }

  return { gt, challenge };
}

Method C: Intercept network requests

// When using Puppeteer/Playwright, intercept the GeeTest API call
async function interceptGeeTest(page) {
  return new Promise((resolve) => {
    page.on("response", async (response) => {
      const url = response.url();
      if (
        url.includes("api.geetest.com/register") ||
        url.includes("geetest") ||
        url.includes("gt=")
      ) {
        try {
          const data = await response.json();
          if (data.gt && data.challenge) {
            resolve({ gt: data.gt, challenge: data.challenge });
          }
        } catch {}
      }
    });
  });
}

Step 2: Solve via CaptchaAI

const API_KEY = "YOUR_API_KEY";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveGeeTestV3(gt, challenge, pageurl) {
  // Submit
  const submitResp = await fetch("https://ocr.captchaai.com/in.php", {
    method: "POST",
    body: new URLSearchParams({
      key: API_KEY,
      method: "geetest",
      gt: gt,
      challenge: challenge,
      pageurl: pageurl,
      json: "1",
    }),
  });
  const submitData = await submitResp.json();

  if (submitData.status !== 1) {
    throw new Error(`Submit error: ${submitData.request}`);
  }

  const taskId = submitData.request;
  console.log(`Task ID: ${taskId}`);

  // Poll
  for (let i = 0; i < 30; i++) {
    await sleep(5000);

    const pollResp = await fetch(
      `https://ocr.captchaai.com/res.php?${new URLSearchParams({
        key: API_KEY,
        action: "get",
        id: taskId,
        json: "1",
      })}`
    );
    const pollData = await pollResp.json();

    if (pollData.status === 1) {
      // Parse the GeeTest response
      const response =
        typeof pollData.request === "string"
          ? JSON.parse(pollData.request)
          : pollData.request;

      return {
        geetest_challenge: response.geetest_challenge,
        geetest_validate: response.geetest_validate,
        geetest_seccode: response.geetest_seccode,
      };
    }

    if (pollData.request === "ERROR_CAPTCHA_UNSOLVABLE") {
      throw new Error("GeeTest unsolvable");
    }
  }

  throw new Error("Solve timed out");
}

Step 3: Submit the response

async function submitGeeTestForm(url, formData, geetestResponse) {
  const body = new URLSearchParams({
    ...formData,
    geetest_challenge: geetestResponse.geetest_challenge,
    geetest_validate: geetestResponse.geetest_validate,
    geetest_seccode: geetestResponse.geetest_seccode,
  });

  const resp = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    },
    body,
  });

  return {
    status: resp.status,
    body: await resp.json().catch(() => resp.text()),
  };
}

Complete flow example

async function loginWithGeeTest(loginUrl, apiUrl, credentials) {
  // Step 1: Get GeeTest parameters
  const { gt, challenge } = await extractGeeTestFromAPI(apiUrl);
  console.log(`GT: ${gt}`);
  console.log(`Challenge: ${challenge}`);

  // Step 2: Solve
  const response = await solveGeeTestV3(gt, challenge, loginUrl);
  console.log("GeeTest solved:", Object.keys(response));

  // Step 3: Submit
  const result = await submitGeeTestForm(loginUrl, credentials, response);
  console.log(`Result: ${result.status}`);

  return result;
}

// Usage
const result = await loginWithGeeTest(
  "https://example.com/login",
  "https://example.com/api/captcha/init",
  { username: "user", password: "pass123" }
);

Production solver class

class GeeTestSolver {
  #apiKey;

  constructor(apiKey) {
    this.#apiKey = apiKey;
  }

  async solve(gt, challenge, pageurl) {
    const taskId = await this.#submit(gt, challenge, pageurl);
    return await this.#poll(taskId);
  }

  async #submit(gt, challenge, pageurl) {
    const resp = await fetch("https://ocr.captchaai.com/in.php", {
      method: "POST",
      body: new URLSearchParams({
        key: this.#apiKey,
        method: "geetest",
        gt,
        challenge,
        pageurl,
        json: "1",
      }),
    });
    const data = await resp.json();
    if (data.status !== 1) throw new Error(`Submit: ${data.request}`);
    return data.request;
  }

  async #poll(taskId) {
    const params = new URLSearchParams({
      key: this.#apiKey,
      action: "get",
      id: taskId,
      json: "1",
    });

    for (let i = 0; i < 30; i++) {
      await new Promise((r) => setTimeout(r, 5000));
      const resp = await fetch(`https://ocr.captchaai.com/res.php?${params}`);
      const data = await resp.json();

      if (data.status === 1) {
        const result =
          typeof data.request === "string"
            ? JSON.parse(data.request)
            : data.request;
        return {
          geetest_challenge: result.geetest_challenge,
          geetest_validate: result.geetest_validate,
          geetest_seccode: result.geetest_seccode,
        };
      }
      if (data.request === "ERROR_CAPTCHA_UNSOLVABLE") {
        throw new Error("Unsolvable");
      }
    }
    throw new Error("Timed out");
  }
}

Troubleshooting

Symptom Cause Fix
challenge is empty Challenge not loaded yet Fetch challenge from API first
Response fields are undefined JSON parsing issue Check typeof data.request
Server rejects response Stale challenge token Get fresh challenge before solving
ERROR_BAD_PARAMETERS Missing gt or challenge Verify both are 32-char hex strings
Different challenge on each load Normal — challenge is one-time Always get fresh challenge

Frequently asked questions

What's the difference between GeeTest v3 and v4?

v3 uses gt + challenge parameters. v4 uses captcha_id and has a different response format. Use method=geetest for v3 and method=geetest_v4 for v4.

Why does the challenge change every time?

The challenge is a one-time token generated by GeeTest's server. You must extract a fresh challenge for each solve attempt.

What does geetest_seccode contain?

It's typically {geetest_validate}|jordan — a concatenation of the validate value and a fixed suffix.


Summary

Solve GeeTest v3 with Node.js and CaptchaAI: extract gt and challenge from the page or API, solve with method=geetest, and submit the three response fields (geetest_challenge, geetest_validate, geetest_seccode).

Discussions (0)

No comments yet.

Related Posts

API Tutorials Solve GeeTest v3 CAPTCHA with Node.js and CaptchaAI
Step-by-step Node.js tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API.

Step-by-step Node.js tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API. Include...

Automation Testing GeeTest v3
Mar 04, 2026
Troubleshooting GeeTest v3 Error Codes: Complete Troubleshooting Reference
Complete reference for Gee Test v 3 error codes — from registration failures to validation errors — with causes, fixes, and Captcha AI-specific troubleshooting.

Complete reference for Gee Test v 3 error codes — from registration failures to validation errors — with cause...

Automation Testing GeeTest v3
Apr 08, 2026
Troubleshooting Common GeeTest v3 Errors and Fixes
Diagnose the most common Gee Test v 3 errors — stale challenge, bad parameters, validation failures — and fix them with practical troubleshooting steps.

Diagnose the most common Gee Test v 3 errors — stale challenge, bad parameters, validation failures — and fix...

Automation Testing GeeTest v3
Jan 24, 2026
Explainers GeeTest v3 Challenge-Response Workflow: Technical Deep Dive
A technical deep dive into Gee Test v 3's challenge-response workflow — the registration API, challenge token exchange, slider verification, and how Captcha AI...

A technical deep dive into Gee Test v 3's challenge-response workflow — the registration API, challenge token...

Automation Testing GeeTest v3
Mar 02, 2026
Explainers How GeeTest v3 CAPTCHA Works
how Gee Test v 3 CAPTCHA works.

Learn how Gee Test v 3 CAPTCHA works. Understand slide puzzles, icon challenges, the verification flow, and ho...

Automation Testing GeeTest v3
Feb 13, 2026
Tutorials GeeTest Token Injection in Browser Automation Frameworks
how to inject Gee Test v 3 solution tokens into Playwright, Puppeteer, and Selenium — including the three-value response, callback triggering, and form submissi...

Learn how to inject Gee Test v 3 solution tokens into Playwright, Puppeteer, and Selenium — including the thre...

Automation Python Testing
Jan 18, 2026
API Tutorials Solve GeeTest v3 CAPTCHA with Python and CaptchaAI
Step-by-step Python tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API.

Step-by-step Python tutorial for solving Gee Test v 3 slide puzzle CAPTCHAs using the Captcha AI API. Includes...

Automation Python Testing
Mar 23, 2026
Tutorials Browser Console CAPTCHA Detection: Finding Sitekeys and Parameters
Use browser Dev Tools to detect CAPTCHA types, extract sitekeys, and find parameters needed for Captcha AI API requests.

Use browser Dev Tools to detect CAPTCHA types, extract sitekeys, and find all parameters needed for Captcha AI...

Automation reCAPTCHA v2 Cloudflare Turnstile
Mar 25, 2026
API Tutorials How to Solve GeeTest v3 Using API
Complete guide to solving Gee Test v 3 slide CAPTCHAs with Captcha AI.

Complete guide to solving Gee Test v 3 slide CAPTCHAs with Captcha AI. Includes parameter extraction, Python a...

Automation Testing GeeTest v3
Jan 13, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Automation Python reCAPTCHA v2
Apr 08, 2026
Tutorials Using Fiddler to Inspect CaptchaAI API Traffic
How to use Fiddler Everywhere and Fiddler Classic to capture, inspect, and debug Captcha AI API requests and responses — filters, breakpoints, and replay for tr...

How to use Fiddler Everywhere and Fiddler Classic to capture, inspect, and debug Captcha AI API requests and r...

Automation Python All CAPTCHA Types
Mar 05, 2026