Skip to main content
Peak — solve Cloudflare Turnstile & the 5s challenge in ~1s

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

  1. process_response inspects every response for Turnstile markers (cf-turnstile, the challenges.cloudflare.com/turnstile script, the "Just a moment" interstitial).
  2. On a match it extracts the data-sitekey and the request URL.
  3. It calls Peak: POST https://api.peak.fo/solve with {"task_type": "turnstiletask", "sitekey": ..., "url": ...} and header X-API-Key.
  4. It re-issues the original request with the returned token injected as the cf-turnstile-response form field, and also on request.meta["cf_turnstile_response"] for custom submits.
  5. A meta flag 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)

Source distribution for scrapy-turnstile 0.2.1
File Size Uploaded
scrapy_turnstile-0.2.1.tar.gz 449.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for scrapy-turnstile 0.2.1
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page