Solving Invisible CAPTCHAs: Turnstile, reCAPTCHA v3, and GeeTest v4

Solving Invisible CAPTCHAs: Turnstile, reCAPTCHA v3, and GeeTest v4

Posted on 2026-08-12 | Last Updated: 2026-08-13 | 3 min read | Category: learning-advanced | By DeathByCaptcha Engineering Team

Learning Advanced


Invisible CAPTCHAs never show a challenge. They score the visitor in the background and only escalate to a puzzle when something looks wrong. Solving them requires understanding how the token is produced and consumed, not just answering a puzzle.

This advanced Learning Path article covers Turnstile, reCAPTCHA v3, and GeeTest v4.

How Invisible CAPTCHAs Work

Invisible CAPTCHAs run JavaScript that collects signals: mouse movements, keyboard timing, browser fingerprint, IP reputation, and page behavior. It produces a score or token. The site decides what to do:

  • Good score: proceed silently.
  • Suspicious: show an interactive challenge.
  • Bad: block outright.

Your automation needs to obtain a valid token in the context of the real page.

Cloudflare Turnstile

Turnstile is type 12. It can run non-interactively, which means the solver returns a token without showing the user anything. The payload is minimal:

result = client.decode({
    "sitekey": "SITE_KEY",
    "pageurl": "https://example.com",
}, type=12, timeout=60)

if result:
    token = result.text
    # inject into <input name="cf-turnstile-response">

Turnstile tokens are single-use and validated against the page URL. Inject the token and submit the form on the same page that generated the challenge.

In Node.js:

const result = await client.decode({
    sitekey: 'SITE_KEY',
    pageurl: 'https://example.com',
}, 60, 12);

if (result) {
    const token = result.text;
    // inject into <input name="cf-turnstile-response">
}

And in C#:

string turnstileParams = "{\"sitekey\":\"SITE_KEY\",\"pageurl\":\"https://example.com\"}";
Captcha result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 12 }, { "turnstile_params", turnstileParams } });

if (result != null)
{
    string token = result.Text;
    // inject into <input name="cf-turnstile-response">
}

reCAPTCHA v3

reCAPTCHA v3 (type 5) is always running in the background. Sites typically require a token with a specific action name:

result = client.decode({
    "googlekey": "SITE_KEY",
    "pageurl": "https://example.com/checkout",
    "action": "checkout",
}, type=5, timeout=60)

The action must match what the site's JavaScript sends. Inspect the site code to find the action string, then reuse it exactly.

In Node.js:

const result = await client.decode({
    googlekey: 'SITE_KEY',
    pageurl: 'https://example.com/checkout',
    action: 'checkout',
}, 60, 5);

And in C#:

string recaptchaParams = "{\"googlekey\":\"SITE_KEY\",\"pageurl\":\"https://example.com/checkout\",\"action\":\"checkout\"}";
Captcha result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 5 }, { "token_params", recaptchaParams } });

GeeTest v4

GeeTest v4 (type 9) uses a captcha_id and an optional action, plus the page URL. v3 (type 8) instead uses gt and a challenge value. Because the parameters differ, always confirm which generation the site uses:

# v3
result = client.decode({
    "gt": "GT_VALUE",
    "challenge": "CHALLENGE_VALUE",
    "pageurl": "https://example.com",
}, type=8, timeout=60)

# v4
result = client.decode({
    "captcha_id": "CAPTCHA_ID",
    "action": "login",
    "pageurl": "https://example.com",
}, type=9, timeout=60)

In Node.js:

// v3
let result = await client.decode({
    gt: 'GT_VALUE',
    challenge: 'CHALLENGE_VALUE',
    pageurl: 'https://example.com',
}, 60, 8);

// v4
result = await client.decode({
    captcha_id: 'CAPTCHA_ID',
    action: 'login',
    pageurl: 'https://example.com',
}, 60, 9);

And in C#:

// v3
string geetestParams = "{\"gt\":\"GT_VALUE\",\"challenge\":\"CHALLENGE_VALUE\",\"pageurl\":\"https://example.com\"}";
Captcha result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 8 }, { "geetest_params", geetestParams } });

// v4
string geetestV4Params = "{\"captcha_id\":\"CAPTCHA_ID\",\"action\":\"login\",\"pageurl\":\"https://example.com\"}";
result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 9 }, { "geetest_params", geetestV4Params } });

Injecting Tokens Correctly

For all invisible CAPTCHAs the final step is identical: put the token into the hidden response field and let the page's own callback do the rest.

document.querySelector('[name=cf-turnstile-response]').value = token;

Some widgets listen for a JavaScript callback. After setting the field, invoke the callback with the token if the site registered one. Without this, the form submission may still be rejected.

Why Invisible CAPTCHAs Fail in Automation

  • Wrong action name in reCAPTCHA v3.
  • Token from the wrong page: the URL must match exactly, including protocol.
  • Missing callback invocation after injection.
  • IP mismatch between the solving session and the page session. Use a matching proxy.

Key Takeaways

  • Invisible CAPTCHAs are type 5 (reCAPTCHA v3), 8 (GeeTest v3), 9 (GeeTest v4), and 12 (Turnstile).
  • Match the action and page URL exactly.
  • Inject the token into the hidden field and trigger the site callback.
  • Keep the solving IP consistent with the browsing IP.

The final article in the Learning Path covers production-grade automation patterns and how to stay reliable at scale.

Common pitfalls

  • Using a CAPTCHA solving service for illegitimate purposes instead of legitimate automation and testing.
  • Hard-coding credentials or API keys in client-side code that users can inspect.
  • Sending the wrong CAPTCHA type parameter, which returns incorrect or empty responses.
  • Failing to poll for the solution status and not handling timeouts gracefully.
  • Scaling automation without monitoring error rates, response times, and CAPTCHA type coverage.
DBC
Written by DeathByCaptcha Engineering Team
DeathByCaptcha engineers build and operate the CAPTCHA solving technology behind this site. Articles are written by our technical team and checked for accuracy before publishing.
Reviewed by DeathByCaptcha Editorial Team

Start solving CAPTCHAs today

Create a free account and get started with the DeathByCaptcha API in minutes. No credit card required.

Create a free account


Status: OK

Servers are fully operational with faster than average response time.
  • Average solving time
  • 6 seconds - Normal CAPTCHAs (1 min. ago)
  • 26 seconds - reCAPTCHA V2, V3 (1 min. ago)
  • 7 seconds - others (1 min. ago)
Chrome and Firefox logos
Browser extensions available

Updates

  1. May 13: Crypto payments got better! You can now purchase your CAPTCHAs using cryptocurrency through the Hekelet payment processor at https://deathbycaptcha.com/user-pay and receive an extra 20% FREE CAPTCHA credit with every package purchased this way.
  2. Apr 15: GitHub Updates: We’ve upgraded our libraries, expanded sample code, enhanced documentation, and added support for C++ and Go, making integration smoother than ever. Explore what’s new at github.com/deathbycaptcha!
  3. Jan 27: RESOLVED - If your email to one of our official addresses ([email protected], [email protected], or [email protected]) has bounced or you haven’t received a response, please try resending it or reach out via our Live Chat Support at https://deathbycaptcha.com/es/contact.

  4. Previous updates…

Support

Our system is designed to be completely user-friendly and easy-to-use. Should you have any trouble with it, simply email us at DBC technical support emailcom, and a support agent will get back to you as soon as possible.

Live Support

Available Monday to Friday (10am to 4pm EST) Live support image. Link to live support page