Quick verdict: Cloudscraper is a drop-in replacement for the requests library that handles Cloudflare's old JavaScript challenge ("Just a moment..."). It is fast, runs without a browser, and ships in 5 minutes. It does NOT bypass Cloudflare Turnstile (the modern CAPTCHA), Bot Fight Mode, or interactive challenges — for those you need FlareSolverr or a real browser via Playwright. Pair Cloudscraper with rotating residential proxies and it covers maybe 60-70% of Cloudflare-protected targets in 2026.
When Cloudflare's "I'm Under Attack" mode fires, the server returns an HTML page with embedded JavaScript that computes a token. The browser runs the JS, posts the token back, and gets a clearance cookie. Cloudscraper reproduces that JS computation in pure Python, fetches the cookie, and lets you continue with normal HTTP requests. No headless Chrome, no 200 MB memory footprint — just pip install cloudscraper and a near-identical API to requests.
The library has been maintained since 2017 and forked dozens of times as Cloudflare iterates. The current generation handles the version 2 (v2) JavaScript challenge but not Turnstile.
pip install cloudscraperimport cloudscraper
scraper = cloudscraper.create_scraper()
response = scraper.get("https://example.com")
print(response.status_code)
print(response.text[:200])That's it. The scraper object exposes the same .get(), .post(), .headers, .cookies as requests.Session. Existing scraping code that uses requests can usually swap to Cloudscraper with a single import change.
Cloudflare blocks individual IPs aggressively. Even with the JS challenge handled, requests from a single datacenter IP get rate-limited within a few hundred requests. Pair Cloudscraper with rotating residential proxies for production scraping:
import cloudscraper
scraper = cloudscraper.create_scraper()
proxies = {
"http": "http://USER:[email protected]:8000",
"https": "http://USER:[email protected]:8000",
}
r = scraper.get("https://target.com", proxies=proxies, timeout=30)
print(r.status_code)Use Premium Residential ($2.75/GB, 130M+ IPs, sticky sessions up to 8 hours) for sites that fingerprint by session, or Budget Residential ($1.75/GB, 10M+ IPs) for high-volume scraping where session continuity does not matter.
Cloudscraper accepts a config dict at scraper creation:
scraper = cloudscraper.create_scraper(
browser={
"browser": "chrome",
"platform": "windows",
"mobile": False,
},
delay=10, # extra delay before solving challenge
interpreter="js2py", # nodejs | js2py | native
)Browser: the User-Agent + JS engine fingerprint Cloudscraper presents. Match this to the OS your proxies are simulating. Mobile fingerprints from desktop residential IPs look suspicious.
Interpreter: Cloudscraper can run the JS challenge using js2py (pure Python, slow but no dependencies), nodejs (fastest if Node is installed), or native (Python implementation of the math). Default is native; switch to nodejs if you hit interpretation errors.
If a target serves the legacy hCaptcha or reCAPTCHA v2 (still common), Cloudscraper can hand it off to a solver service:
scraper = cloudscraper.create_scraper(
captcha={
"provider": "2captcha",
"api_key": "YOUR_2CAPTCHA_KEY",
}
)Supported providers: 2captcha, anticaptcha, capmonster, deathbycaptcha. See the CAPTCHA solver comparison for pricing and accuracy.
Cloudscraper returns a 403 or HTML body containing "Just a moment..." when it cannot solve the challenge. Common reasons:
requests and Cloudscraper both use Python's default TLS, which has a fingerprint Cloudflare flags. Use curl_cffi instead for impersonating real browser TLS.| Scenario | Tool |
|---|---|
| Old "Just a moment..." page | Cloudscraper (this guide) |
| Turnstile CAPTCHA | FlareSolverr + 2Captcha |
| TLS fingerprint blocks | curl_cffi with Chrome impersonation |
| Interactive challenge | Playwright/Puppeteer with stealth plugin |
| Heavy JS rendering | Playwright with rotating residential proxies |
For scale, rotate sessions and IPs together. A new cloudscraper.create_scraper() object gets a fresh cookie jar — combine that with a fresh residential IP for each session:
import cloudscraper, random
def fetch(url):
session_id = random.randint(0, 100000)
proxy = f"http://USER-session-{session_id}:[email protected]:8000"
scraper = cloudscraper.create_scraper(
browser={"browser": "chrome", "platform": "windows"},
)
return scraper.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
for url in urls:
r = fetch(url)
print(r.status_code, len(r.content))The session-{id} token in the proxy username is SpyderProxy's sticky-session syntax — the same IP for that session ID stays for up to 8 hours, then rotates.
cloudscraper.exceptions.CloudflareChallengeError — could not solve challenge. Try a different browser fingerprint or upgrade Cloudscraper.cloudscraper.exceptions.CloudflareCode1020 — you are firewall-blocked, not challenged. IP is on Cloudflare's list; rotate proxies. See Cloudflare error codes explained.403 Forbidden after success — passed the JS challenge but the application layer blocks you. Add cookies, headers, or session tokens that a real browser would have.Bypassing Cloudflare on a site is not inherently illegal in the US (per the HiQ v. LinkedIn ruling on public data) but the site's ToS may forbid it. Cloudscraper itself is a research/penetration testing tool. Use it on sites you own or have written permission to scrape; respect robots.txt; rate-limit your requests; and never scrape personal data behind a login without consent (GDPR/CCPA).
Next steps: if Cloudscraper is not enough for your target, the Cloudflare bypass guide covers FlareSolverr setup and the curl_cffi approach. For scraping at scale, see rotating proxies with Python requests.