Tutorials

Solving Cloudflare Turnstile with Node.js and CaptchaAI

Cloudflare Turnstile is replacing reCAPTCHA on many sites. This guide covers the complete Node.js flow: detect Turnstile, extract the sitekey, solve via CaptchaAI, and submit the token.


Prerequisites

  • Node.js 18+ (native fetch)
  • CaptchaAI API key

Step 1: Detect and extract the sitekey

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

  // Method 1: data-sitekey attribute on Turnstile div
  const divMatch = html.match(
    /class=["'][^"]*cf-turnstile[^"]*["'][^>]*data-sitekey=["']([0-9x][A-Za-z0-9_-]+)["']/
  );
  if (divMatch) return divMatch[1];

  // Method 2: data-sitekey on any element (Turnstile keys start with 0x)
  const attrMatch = html.match(
    /data-sitekey=["'](0x[A-Za-z0-9_-]+)["']/
  );
  if (attrMatch) return attrMatch[1];

  // Method 3: In JavaScript turnstile.render call
  const jsMatch = html.match(
    /turnstile\.render\s*\([^,]+,\s*\{[^}]*sitekey\s*:\s*["']([0-9x][A-Za-z0-9_-]+)["']/
  );
  if (jsMatch) return jsMatch[1];

  // Method 4: Generic sitekey in inline script
  const inlineMatch = html.match(
    /sitekey\s*:\s*["'](0x[A-Za-z0-9_-]+)["']/
  );
  if (inlineMatch) return inlineMatch[1];

  return null;
}

Step 2: Solve Turnstile via CaptchaAI

const API_KEY = "YOUR_API_KEY";

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

async function solveTurnstile(sitekey, pageurl, action = null) {
  // Submit task
  const submitData = {
    key: API_KEY,
    method: "turnstile",
    sitekey: sitekey,
    pageurl: pageurl,
    json: "1",
  };

  if (action) {
    submitData.action = action;
  }

  const submitResp = await fetch("https://ocr.captchaai.com/in.php", {
    method: "POST",
    body: new URLSearchParams(submitData),
  });
  const submitResult = await submitResp.json();

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

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

  // Poll for result
  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 pollResult = await pollResp.json();

    if (pollResult.status === 1) {
      return pollResult.request;
    }

    if (pollResult.request === "ERROR_CAPTCHA_UNSOLVABLE") {
      throw new Error("Turnstile unsolvable");
    }
  }

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

Step 3: Submit the token

async function submitTurnstileForm(url, formData, token) {
  const body = new URLSearchParams({
    ...formData,
    "cf-turnstile-response": token,
  });

  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) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
    },
    body,
  });

  return {
    status: resp.status,
    body: await resp.text(),
  };
}

Complete login flow

async function loginWithTurnstile(loginUrl, credentials) {
  // Step 1: Extract sitekey
  const sitekey = await extractTurnstileSitekey(loginUrl);
  if (!sitekey) {
    throw new Error("Turnstile sitekey not found");
  }
  console.log(`Sitekey: ${sitekey}`);

  // Step 2: Solve Turnstile
  const token = await solveTurnstile(sitekey, loginUrl);
  console.log(`Token: ${token.substring(0, 50)}...`);

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

  return result;
}

// Usage
const result = await loginWithTurnstile("https://example.com/login", {
  email: "user@example.com",
  password: "pass123",
});

Production solver class

class TurnstileSolver {
  #apiKey;

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

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

  async detectAndSolve(url) {
    const sitekey = await this.#detect(url);
    if (!sitekey) throw new Error("No Turnstile found");
    return await this.solve(sitekey, url);
  }

  async #detect(url) {
    const resp = await fetch(url, {
      headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0" },
    });
    const html = await resp.text();
    const match = html.match(/data-sitekey=["'](0x[A-Za-z0-9_-]+)["']/);
    return match ? match[1] : null;
  }

  async #submit(sitekey, pageurl, options) {
    const body = new URLSearchParams({
      key: this.#apiKey,
      method: "turnstile",
      sitekey,
      pageurl,
      json: "1",
      ...(options.action && { action: options.action }),
      ...(options.cdata && { data: options.cdata }),
    });

    const resp = await fetch("https://ocr.captchaai.com/in.php", {
      method: "POST",
      body,
    });
    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) return data.request;
      if (data.request === "ERROR_CAPTCHA_UNSOLVABLE") {
        throw new Error("Unsolvable");
      }
    }
    throw new Error("Timed out");
  }
}

// Usage
const solver = new TurnstileSolver("YOUR_API_KEY");
const token = await solver.detectAndSolve("https://example.com/login");

Turnstile with action and cData parameters

Some Turnstile implementations include action and cData parameters:

// Extract action from the page
function extractTurnstileAction(html) {
  const match = html.match(
    /data-action=["']([^"']+)["']|action\s*:\s*["']([^"']+)["']/
  );
  return match ? match[1] || match[2] : null;
}

// Solve with action
const token = await solver.solve(sitekey, pageurl, {
  action: "login",
  cdata: "session_abc123",
});

Server-side token verification

If you're building a server that verifies Turnstile tokens:

async function verifyTurnstileToken(token, ip) {
  const resp = await fetch(
    "https://challenges.cloudflare.com/turnstile/v0/siteverify",
    {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        secret: "YOUR_TURNSTILE_SECRET_KEY",
        response: token,
        remoteip: ip,
      }),
    }
  );

  const data = await resp.json();
  return data.success;
}

Troubleshooting

Symptom Cause Fix
Sitekey starts with 6Le That's reCAPTCHA, not Turnstile Use method=userrecaptcha
Token rejected Wrong sitekey or expired Re-extract sitekey, submit faster
No sitekey found Turnstile loaded via JavaScript Use Puppeteer/Playwright instead
ERROR_BAD_PARAMETERS Missing sitekey or pageurl Check both are present
403 response after submit Bot detection on headers Use realistic User-Agent

Frequently asked questions

How is Turnstile different from reCAPTCHA?

Turnstile is Cloudflare's CAPTCHA replacement. It's typically invisible, faster to solve, and uses the method=turnstile API parameter instead of method=userrecaptcha.

Do I need to specify the action parameter?

Only if the site's Turnstile implementation uses it. Check for data-action in the HTML or action: in JavaScript.

What's the success rate for Turnstile solving?

CaptchaAI achieves 100% success rate on Turnstile challenges.


Summary

Solve Cloudflare Turnstile with Node.js and CaptchaAI: extract the sitekey (starts with 0x), solve via the method=turnstile API, and submit the token as cf-turnstile-response.

Discussions (0)

No comments yet.

Related Posts

Tutorials Node.js Playwright + CaptchaAI Complete Integration
Complete guide to integrating Captcha AI with Node.js Playwright.

Complete guide to integrating Captcha AI with Node.js Playwright. Solve re CAPTCHA, Turnstile, and image CAPTC...

Automation Cloudflare Turnstile Node.js
Apr 05, 2026
Tutorials Node.js Puppeteer + CaptchaAI Advanced Patterns
Advanced Puppeteer + Captcha AI integration patterns: stealth mode, request interception, iframe handling, multi-page flows, and parallel solving.

Advanced Puppeteer + Captcha AI integration patterns: stealth mode, request interception, iframe handling, mul...

Automation Cloudflare Turnstile Node.js
Feb 24, 2026
Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Automation Python reCAPTCHA v2
Apr 08, 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 CAPTCHA Solving Fallback Chains
Implement fallback chains for CAPTCHA solving with Captcha AI.

Implement fallback chains for CAPTCHA solving with Captcha AI. Cascade through solver methods, proxy pools, an...

Automation Python reCAPTCHA v2
Apr 06, 2026
Use Cases Multi-Step Workflow Automation with CaptchaAI
Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at scale.

Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at sc...

Automation Python reCAPTCHA v2
Apr 06, 2026
Integrations Puppeteer Stealth + CaptchaAI: Reliable Browser Automation
Standard Puppeteer gets detected immediately by anti-bot systems.

Standard Puppeteer gets detected immediately by anti-bot systems. `puppeteer-extra-plugin-stealth` patches the...

Automation reCAPTCHA v2 Cloudflare Turnstile
Apr 05, 2026
Comparisons WebDriver vs Chrome DevTools Protocol for CAPTCHA Automation
Compare Web Driver and Chrome Dev Tools Protocol (CDP) for CAPTCHA automation — detection, performance, capabilities, and when to use each with Captcha AI.

Compare Web Driver and Chrome Dev Tools Protocol (CDP) for CAPTCHA automation — detection, performance, capabi...

Automation Python reCAPTCHA v2
Mar 27, 2026
Troubleshooting ERROR_PAGEURL: URL Mismatch Troubleshooting Guide
Fix ERROR_PAGEURL when using Captcha AI.

Fix ERROR_PAGEURL when using Captcha AI. Diagnose URL mismatch issues, handle redirects, SPAs, and dynamic URL...

Automation Python reCAPTCHA v2
Mar 23, 2026
Tutorials Extracting reCAPTCHA Parameters from Page Source
Extract re CAPTCHA parameters from any web page — sitekey, action, data-s, enterprise flag, and version — using regex, DOM queries, and network interception.

Extract all re CAPTCHA parameters from any web page — sitekey, action, data-s, enterprise flag, and version —...

Python reCAPTCHA v2 Web Scraping
Apr 07, 2026
Tutorials Handling Multiple CAPTCHAs on a Single Page
how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI.

Learn how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI. Covers multi-iframe ext...

Python reCAPTCHA v2 Cloudflare Turnstile
Apr 09, 2026
Tutorials Streaming Batch Results: Processing CAPTCHA Solutions as They Arrive
Process CAPTCHA solutions the moment they arrive instead of waiting for tasks to complete — use async generators, event emitters, and callback patterns for stre...

Process CAPTCHA solutions the moment they arrive instead of waiting for all tasks to complete — use async gene...

Automation Python All CAPTCHA Types
Apr 07, 2026