Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cloudflare Turnstile Solver API

npm PyPI npm downloads CI License: MIT

Official Node and Python clients for the SolveGate API, which clears Cloudflare Turnstile and Turnstile WAF challenges and returns a token. Both SDKs are dependency-free and wrap auth, the error envelope, 429 backoff and async polling.

Get 1,000 free solves — no card required →

Supported challenges

Challenge gate Notes
Turnstile — managed turnstile The visible checkbox widget
Turnstile — non-interactive turnstile Visible, never asks for a click
Turnstile — invisible turnstile No widget; token lands in a hidden input
Turnstile WAF challenge page waf The full-page "Just a moment…" interstitial

Not supported

reCAPTCHA (any version), hCaptcha, GeeTest, FunCaptcha, Arkose Labs, DataDome, Imperva and AWS WAF. If any of those appear in your flow, this is the wrong tool and a general-purpose service is the right one. The narrow surface is deliberate — it is why solves land in about a second.

Intended use

Use this against properties you own or are explicitly authorised to test: your own staging and production apps, E2E and QA suites, CI pipelines, uptime and synthetic monitoring, and anti-bot configuration testing.

If the site is yours, read ## Testing without solving first — Cloudflare publishes dummy sitekeys that make this SDK unnecessary in CI, and that is the better answer for most test suites.

Pointing it at third-party properties in breach of their terms is prohibited by the Acceptable Use Policy and is enforced. See POLICY.md.

Install

npm install solvegate        # Node 18+
pip install solvegate        # Python 3.9+

Quickstart — Node / TypeScript

import { SolveGate } from "solvegate";

const sg = new SolveGate(process.env.SOLVEGATE_KEY!); // sk_live_… or a free sk_test_… sandbox key

// Synchronous: wait for the token.
const solve = await sg.solve({
  gate: "turnstile",
  sitekey: "0x4AAAAAAAAA_target",
  url: "https://app.example.com",
});
console.log(solve.token, solve.solve_ms);

// Asynchronous: submit, then poll.
const pending = await sg.solve({ gate: "waf", sitekey: "0x4…", url: "…", async: true });
const done = await sg.wait(pending.id, { timeoutMs: 60_000 });

Quickstart — Python

import os
from solvegate import SolveGate

sg = SolveGate(os.environ["SOLVEGATE_KEY"])

solve = sg.solve(
    gate="turnstile",
    sitekey="0x4AAAAAAAAA_target",
    url="https://app.example.com",
)
print(solve.token, solve.solve_ms)

pending = sg.solve(gate="waf", sitekey="0x4…", url="…", async_=True)  # note the trailing underscore
done = sg.wait(pending.id, timeout=60)

Testing without solving

If you control the application under test, you almost never need a solver in CI. Cloudflare publishes dummy sitekeys that work on any domain, including localhost, and return a token formatted XXXX.DUMMY.TOKEN.XXXX.

Key Type Behaviour
1x00000000000000000000AA sitekey, visible Always passes
1x00000000000000000000BB sitekey, invisible Always passes
2x00000000000000000000AB sitekey, visible Always fails
3x00000000000000000000FF sitekey, visible Forces an interactive challenge
1x0000000000000000000000000000000AA secret Always passes validation
2x0000000000000000000000000000000AA secret Always fails validation
3x0000000000000000000000000000000AA secret Returns timeout-or-duplicate

Swap both halves — a test secret key rejects real tokens and a live secret key rejects the dummy token. See Cloudflare's testing docs.

Solving is for what test keys cannot cover: a staging environment whose Turnstile config you do not control, or synthetic monitoring that must exercise the real production path.

Recipes

Playwright — see playwright-turnstile for a fixture that does this properly.

const sitekey = await page.locator("[data-sitekey]").first().getAttribute("data-sitekey");
const { token } = await sg.solve({ gate: "turnstile", sitekey, url: page.url() });
await page.evaluate((t) => {
  document.querySelector<HTMLInputElement>('[name="cf-turnstile-response"]')!.value = t;
}, token);

Puppeteer

const sitekey = await page.$eval("[data-sitekey]", (el) => el.dataset.sitekey);
const { token } = await sg.solve({ gate: "turnstile", sitekey, url: page.url() });
await page.evaluate((t) => {
  document.querySelector('[name="cf-turnstile-response"]').value = t;
}, token);

Selenium (Python)

sitekey = driver.find_element(By.CSS_SELECTOR, "[data-sitekey]").get_attribute("data-sitekey")
token = sg.solve(gate="turnstile", sitekey=sitekey, url=driver.current_url).token
driver.execute_script(
    "document.querySelector('[name=\"cf-turnstile-response\"]').value = arguments[0]", token
)

GitHub Actions — store the key as a secret and prefer a sk_test_ sandbox key in CI:

- run: npm test
  env:
    SOLVEGATE_KEY: ${{ secrets.SOLVEGATE_TEST_KEY }}

See cloudflare-turnstile-e2e-example for a complete runnable pipeline, and pytest-turnstile for the Python equivalent.

API reference

Full reference at solvegate.io/docs · OpenAPI at solvegate.io/openapi.json.

POST /v1/solve

{ "gate": "turnstile", "sitekey": "0x4AAAAAAAAA_target", "url": "https://app.example.com" }

Optional: action (the Turnstile action value), proxy (solve through your own egress IP), async (submit and poll instead of waiting). Send an Idempotency-Key header to make a retry safe.

{
  "id": "slv_8Kd2aF9",
  "status": "solved",
  "gate": "turnstile",
  "token": "0.Xa3f…X9_cleared",
  "solve_ms": 980,
  "expires_at": 1714663200,
  "mode": "live",
  "meter": "credits",
  "billed": true
}

GET /v1/solve/{id} retrieves a solve. Retrieval is free and never re-bills.

Errors

Errors carry a status, a stable code, a message, and whether the attempt was billed.

Status Code Meaning
402 balance_empty Out of credits
402 pass_expired The unlimited pass window ended
403 forbidden_target Target refused by policy
422 unknown_sitekey Sitekey malformed or not found
429 rate_limited Over your rate limit; honour Retry-After
504 solve_timeout Not billed, safe to retry

Both SDKs raise SolveGateError carrying status, code, message and billed.

Metering

Solves draw from prepaid credits, or from an unlimited pass if one is attached to the key. Keys prefixed sk_test_ run in sandbox mode: they return a deterministic token prefixed SANDBOX. and cost nothing, which is what you want in CI.

Rate limits

Responses carry RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy. On 429 both SDKs back off and retry automatically, honouring Retry-After.

Pricing

Prepaid credit packs; one credit is one successful solve.

Pack Price Per 1,000 solves
30k $12 $0.40
100k $25 $0.25
1M $150 $0.15
20M $1,500 $0.075

The first 1,000 solves are free, failed solves are never billed, and credits do not expire. Full ladder at solvegate.io/pricing.

FAQ

Does it solve reCAPTCHA or hCaptcha? No. Turnstile and Turnstile WAF only.

Can I point it at a site I don't control? No — see Intended use.

What counts as a failed solve? Anything that returns no token: a timeout, an internal retry that never lands, or a 5xx on our side. None of them are billed.

Do I need a paid key to run my test suite? Usually not. Use Cloudflare's test sitekeys above, or a free sk_test_ sandbox key.

Support & status

Contact · Status · Issues

License

MIT — see LICENSE.

About

Cloudflare Turnstile solver API for E2E tests, CI pipelines and synthetic monitoring — clears Turnstile (managed, non-interactive, invisible) and Turnstile WAF pages on sites you own or are authorised to test. Zero-dependency Node + Python SDKs. First 1,000 solves free.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages