scrapy-turnstile
scrapy-turnstile is a Scrapy downloader middleware that solves Cloudflare Turnstile automatically. When a request hits a Turnstile-protected page, it detects the challenge, solves it through Peak, and re-issues the request with the token so your spider gets the real page.
Why
Scrapy makes plain HTTP requests. It has no JS engine, so a Turnstile-protected site returns a 403 or the "Just a moment..." interstitial instead of the content you asked for. The old scrapy-cloudflare-middleware only handled the legacy IUAM (I'm Under Attack Mode) JS challenge and is unmaintained; it does nothing for Turnstile, which is what Cloudflare now serves. cloudscraper has the same limitation: it can't solve Turnstile.
This middleware routes the Turnstile challenge to Peak, which returns a valid token. 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
- $0.90 per 1,000 successful Turnstile solves, down to $0.35 at volume
- 1,000 free solves to start, no card. → Get your free API key · Docs · Pricing
Install
pip install scrapy-turnstile
Quickstart
Add the middleware to your project's settings.py and set your Peak key:
# settings.py
DOWNLOADER_MIDDLEWARES = {
"scrapy_turnstile.TurnstileMiddleware": 585,
}
PEAK_API_KEY = "pk_your_api_key" # better: read from an env var
# PEAK_PROXY = "http://user:pass@ip:port" # optional, routed to Peak
Or per-spider:
import os
import scrapy
class ProtectedSpider(scrapy.Spider):
name = "protected"
start_urls = ["https://protected.example.com/"]
custom_settings = {
"DOWNLOADER_MIDDLEWARES": {
"scrapy_turnstile.TurnstileMiddleware": 585,
},
"PEAK_API_KEY": os.environ["PEAK_API_KEY"],
}
def parse(self, response):
# The challenge, if any, was already solved upstream.
yield {"title": response.css("title::text").get()}
export PEAK_API_KEY=pk_your_api_key
scrapy runspider examples/quotes_spider.py
A runnable spider is in examples/quotes_spider.py.
How it works
process_responseinspects every response for Turnstile markers (cf-turnstile, thechallenges.cloudflare.com/turnstilescript, the "Just a moment" interstitial).- On a match it extracts the
data-sitekeyand the request URL. - It calls Peak:
POST https://api.peak.fo/solvewith{"task_type": "turnstiletask", "sitekey": ..., "url": ...}and headerX-API-Key. - It re-issues the original request with the returned token injected as the
cf-turnstile-responseform field, and also onrequest.meta["cf_turnstile_response"]for custom submits. - A
metaflag stops the retried request from being re-processed, so there are no loops.
Peak also supports the Cloudflare 5s interstitial via task_type: "cloudflare5stask".
Settings
| Setting | Default | Purpose |
|---|---|---|
PEAK_API_KEY |
(required) | Your Peak key, format pk_.... |
PEAK_PROXY |
None |
Proxy string forwarded to Peak so the solve matches your crawl IP. |
PEAK_API_URL |
https://api.peak.fo/solve |
Override the solve endpoint. |
PEAK_APP_ID |
None |
Optional app id sent as appId to earn revenue share on each solve. |
PEAK_TURNSTILE_FIELD |
cf-turnstile-response |
Form field the token is injected into. |
PEAK_TURNSTILE_MAX_RETRIES |
2 |
Max solve attempts per request before giving up. |
Earn with your app ID
Set PEAK_APP_ID in your Scrapy settings to earn 5% of every solve your spider makes, paid as Peak solve credit. It is optional and does not change whether a solve succeeds or how fast it returns.
# settings.py
PEAK_APP_ID = "app_your_id" # optional; earns solve credit on each solve
Create an app id at peak.fo/dashboard/developer; details at peak.fo/earn.
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.
Metadata
Release files for scrapy-turnstile 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| scrapy_turnstile-0.2.1.tar.gz | 449.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scrapy_turnstile-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 459.0 kB
Release files / scrapy_turnstile-0.2.1.tar.gz
| Download URL | scrapy_turnstile-0.2.1.tar.gz |
|---|---|
| Size | 449.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3995597f3077c69ee248fda93007caf371e23fe2aa19acb0d78c53db1a8c05d2
|
|
BLAKE2b-256 checksum How to use checksums |
9b30b526133c00ea67ac4133fe30b150100e46f9aecc797239f77f6dc42d58d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Release files / scrapy_turnstile-0.2.1-py3-none-any.whl
| Download URL | scrapy_turnstile-0.2.1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
336fc50dff84e971728eced82d8842ad089644b6977d772747a8d8eec0239db5
|
|
BLAKE2b-256 checksum How to use checksums |
4a27addee0591d5f77b49243f36a47db119016f7a00baacec487d799ac33d5b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|