How to Bypass Cloudflare Turnstile with Python

A plain Python requests script can't pass Turnstile. Here's the few-line pattern that does: get a token from a solving API and submit it, with the full code.

You wrote a clean Python scraper, pointed it at the target, and Cloudflare Turnstile stopped it cold. Your requests call comes back with a challenge page instead of data, and no amount of retrying changes it. Here's why, and the exact code to get past it.

The short version: a plain HTTP client can't pass Turnstile because it can't run the JavaScript the widget uses to score the browser. So you don't fight the widget. You get a valid cf-turnstile-response token from a solving API and submit it with your request. That's the whole trick, and it's a few lines.

Why requests alone can't do it

Turnstile isn't a puzzle you answer, it's a script that runs in a real browser and scores how browser-like the environment looks. A bare requests or httpx call has no JavaScript engine, so it can't run that script at all, which means it can never produce the token the server wants. The server sees a missing or invalid cf-turnstile-response and rejects you. This is also why time.sleep() and retry loops do nothing: nothing about the retry changes what Cloudflare is scoring.

If you want the full background on that token, read the cf-turnstile-response token, explained.

The approach that works

Two moving parts. First, you need the target's sitekey (the public Turnstile identifier on the page). If you're not sure how to get it, it's a two-minute job covered in how to find a Turnstile sitekey. Second, you send the page URL, the sitekey, and a proxy to a solving API, get a token back, and submit it.

import requests

API_KEY = "pk_your_api_key"
TARGET  = "https://target.com/login"
SITEKEY = "0x4AAAAAAAxxxxxxxx"
PROXY   = "http://user:pass@ip:port"

# 1) get a Turnstile token (comes back in about a second)
resp = requests.post(
    "https://api.peak.fo/solve",
    headers={"X-API-Key": API_KEY},
    json={
        "task_type": "turnstiletask",
        "url": TARGET,
        "sitekey": SITEKEY,
        "proxy": PROXY,
    },
    timeout=30,
).json()

if not resp.get("success"):
    raise RuntimeError(f"solve failed: {resp.get('error')}")

token = resp["data"]["token"]

# 2) submit the token with your request, before it expires (~300s)
r = requests.post(TARGET, data={
    "email": "you@example.com",
    "password": "...",
    "cf-turnstile-response": token,
})
print(r.status_code)

The token you get back is a normal cf-turnstile-response value. The server can't tell it apart from one a browser produced, because it isn't different. You pay only when a solve lands, so a miss costs nothing.

Where people get it wrong

  • Expired token. Turnstile tokens are single-use and last around 300 seconds. Solve right before the request that needs it, not at the top of a long script.
  • Reused token. Each submission needs a fresh token. The server redeems it once.
  • Wrong sitekey. A token solved against one sitekey won't validate on a different page.
  • Mismatched IP. Some deployments tie the token to the solving context. Solve through the same proxy you submit from, and pass the action if the widget sets one.

When you need a browser instead

If your flow has to stay on the page and interact with it after the challenge clears, driving a real browser and injecting the token can make more sense. That trade-off is covered in solving API vs headless browser, and the injection pattern in handling Turnstile in Playwright and Puppeteer. For a straight scrape where you just need the token, the code above is all it takes.

FAQ

Can you bypass Cloudflare Turnstile with Python requests?

Not with requests alone, because it can't run the JavaScript Turnstile scores. You get a valid cf-turnstile-response token from a solving API and submit it with your request. The code above does exactly that in a few lines.

Why does my Turnstile token get rejected?

Almost always one of four things: the token expired (they last ~300s), you reused it (single-use), you solved against the wrong sitekey, or you submitted from a different IP than you solved on. Solve fresh, right before the request, on the same proxy.

How much does it cost?

Peak bills only for successful Turnstile solves, from $1 per 1,000 down to $0.35 at volume. Failed solves are free. See the solver comparison or pricing.

Get Turnstile tokens from Python in one call. Grab a key free at peak.fo.

Read more