turnstile-curl
turnstile-curl is a curl_cffi companion that solves Cloudflare Turnstile without a browser, so a curl_cffi request that hits a Turnstile wall comes back with a real token instead of a 403.
Why
curl_cffi forges a browser's TLS/JA3 handshake perfectly, which is exactly why people reach for it. But it has no JavaScript engine. Cloudflare Turnstile is a JS widget, so a managed or interactive challenge hands curl_cffi an interstitial HTML page or a 403, and there is nothing in-process that can run the challenge. This is the "curl_cffi cloudflare blocked" wall: the handshake is right, the request still fails.
turnstile-curl reads the Turnstile sitekey straight out of the returned markup, sends it to Peak, and gives you back the cf-turnstile-response token. curl_cffi still does the fetching, so you keep its speed and fingerprint. When your setup gets blocked, drop in a Peak API key and it just works.
Powered by Peak
This package uses Peak to solve Turnstile.
- Solve Cloudflare Turnstile & the 5s challenge in about a second
- Pay only for successful solves — from $1 / 1,000
- 1,000 free solves to start, no card.
→ Get your free API key · Docs · Pricing
Install
pip install turnstile-curl curl_cffi
curl_cffi is the transport you fetch pages with. It is an optional dependency here so the library imports cleanly without it; install it to actually fetch.
Quickstart
import os
from turnstile_curl import solve
# Free key at https://peak.fo.?utm_source=github&utm_medium=readme&utm_campaign=packages&utm_content=turnstile-curl Reads PEAK_API_KEY by default.
os.environ.setdefault("PEAK_API_KEY", "pk_your_api_key")
# Fetch the protected page with curl_cffi, read the sitekey, solve via Peak.
token = solve("https://your-target.example/login")
print(token) # -> "0.AgAAA..." (the cf-turnstile-response value)
Send token as the cf-turnstile-response form field (or into the widget callback) on your next request.
Session: solve and resubmit in one shot
TurnstileSession wraps a curl_cffi session and keeps the same browser fingerprint through the whole flow.
from turnstile_curl import TurnstileSession
with TurnstileSession(api_key="pk_your_api_key", impersonate="chrome") as s:
# Just the token:
token = s.solve("https://your-target.example/login")
# Or solve + resubmit the form with the token injected, and keep the
# authenticated response and cookies on the session:
resp = s.solve_and_submit(
"https://your-target.example/login",
submit_url="https://your-target.example/verify",
data={"username": "demo", "password": "demo"},
)
print(resp.status_code)
Already have the sitekey
Skip the fetch and call Peak directly.
from turnstile_curl import PeakClient
peak = PeakClient(api_key="pk_your_api_key")
token = peak.solve_turnstile(
sitekey="0x4AAAAAAADnPIDROrmt1Wwj",
url="https://your-target.example/login",
# proxy="http://user:pass@ip:port", # optional
)
Proxies
Pass proxy="http://user:pass@ip:port" to TurnstileSession, solve(), or PeakClient.solve_turnstile(). The same proxy is used for the page fetch and forwarded to Peak so the token is minted from the IP you will use it on.
How it works
- curl_cffi fetches the page with a forged browser TLS/JA3 fingerprint.
- turnstile-curl parses the sitekey (and
cData/action/pagedatawhen present) from thedata-sitekeycontainer or aturnstile.render(...)call. - It calls
POST https://api.peak.fo/solvewithtask_type: "turnstiletask", the sitekey, and the URL. - Peak returns the token; you resubmit the form with it as
cf-turnstile-response.
Peak also solves the Cloudflare 5s challenge (task_type: "cloudflare5stask"); this package focuses on Turnstile.
API
solve(url, *, api_key=None, impersonate="chrome", proxy=None) -> str— one-shot fetch + solve, returns the token.TurnstileSession(api_key=None, *, impersonate="chrome", proxy=None)—.get(),.solve(url),.detect_challenge(url),.solve_and_submit(url, ...).PeakClient(api_key=None)—.solve(challenge),.solve_turnstile(sitekey, url, ...).parse_challenge(html, url)/find_sitekey(html)— the extractor, if you fetch pages yourself.
The API key is read from PEAK_API_KEY unless you pass api_key=.
Legitimate use
For automation, QA, and scraping public data you are allowed to access. Respect each target's Terms of Service and robots.txt, and rate-limit yourself. Do not use this for credential stuffing or to access data you have no right to.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file turnstile_curl-0.1.0.tar.gz.
File metadata
- Download URL: turnstile_curl-0.1.0.tar.gz
- Upload date:
- Size: 11.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e612ad519de48fd9f7bd6a62f2662319e4bd94fc6cbcc8af54c9b3dd0d821712
|
|
| MD5 |
e5cb417b5aaac30bbfd51a7fda640253
|
|
| BLAKE2b-256 |
b006bc954e5d181e85dad1b5c2abd6c158490775e051807897ac37a9fc2defca
|
File details
Details for the file turnstile_curl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: turnstile_curl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e48f85c65f10cada13e907fa60340692c86aec16dd751d0c060e021080a8acb
|
|
| MD5 |
4a8c94f36e5738e7364e267e67f4d5c5
|
|
| BLAKE2b-256 |
21025a156f21497e67418933dc57e935538e977ef907b12dbc28271cd1881224
|