Tutorial
How to Solve Cloudflare Turnstile Challenges
Learn how to handle Cloudflare Turnstile CAPTCHA challenges when scraping. Covers solving services, browser automation, and managed API solutions.
Cloudflare Turnstile is a CAPTCHA replacement that issues invisible or managed challenges to verify visitors. Unlike traditional CAPTCHAs, Turnstile runs in the background and is designed to be frictionless for real users but difficult for bots.
How Turnstile Works
Turnstile embeds a challenge widget that:
- Collects browser signals (environment, behavior, device data)
- Sends signals to Cloudflare's edge for analysis
- Returns a
cf-turnstile-responsetoken if the check passes - This token must be submitted with the form or API request
<!-- Turnstile widget on the page -->
<div class="cf-turnstile" data-sitekey="0x4AAAAAAA..."></div>
Method 1: ScraperAPI (Recommended)
ScraperAPI handles Turnstile challenges automatically with its rendering engine.
import requests
API_KEY = "YOUR_SCRAPERAPI_KEY"
response = requests.get(
"http://api.scraperapi.com",
params={
"api_key": API_KEY,
"url": "https://turnstile-protected-site.com",
"render": "true"
}
)
print(response.status_code) # 200 - Turnstile solved
print(response.text[:500])
Method 2: Browser Automation
Turnstile challenges can be solved by a real browser that generates valid signals.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # Headed mode important
page = browser.new_page()
page.goto("https://turnstile-protected-site.com")
# Wait for Turnstile to solve (it runs automatically for valid browsers)
page.wait_for_function("""
() => {
const response = document.querySelector('[name="cf-turnstile-response"]');
return response && response.value.length > 0;
}
""", timeout=15000)
# Get the Turnstile token
token = page.evaluate("""
document.querySelector('[name="cf-turnstile-response"]').value
""")
print(f"Turnstile token: {token[:50]}...")
# Now submit the form or use the token in API requests
browser.close()
Method 3: CAPTCHA Solving Service
For programmatic solving, use a CAPTCHA solving service that supports Turnstile.
import requests
import time
# Using a CAPTCHA solving service
def solve_turnstile(sitekey, page_url):
# Submit the challenge
response = requests.post("https://api.capsolver.com/createTask", json={
"clientKey": "YOUR_CAPSOLVER_KEY",
"task": {
"type": "AntiTurnstileTaskProxyLess",
"websiteURL": page_url,
"websiteKey": sitekey
}
})
task_id = response.json()["taskId"]
# Poll for result
while True:
result = requests.post("https://api.capsolver.com/getTaskResult", json={
"clientKey": "YOUR_CAPSOLVER_KEY",
"taskId": task_id
}).json()
if result["status"] == "ready":
return result["solution"]["token"]
time.sleep(2)
# Use the token in your request
token = solve_turnstile("0x4AAAAAAA...", "https://example.com")
Using the Token in Requests
Once you have a Turnstile token, include it in the form submission:
response = requests.post("https://example.com/api/endpoint", data={
"cf-turnstile-response": token,
"other_field": "value"
})
Key Considerations
- Turnstile tokens expire after a few minutes and are single-use
- Headless browsers often fail Turnstile since it checks for real browser signals
- Turnstile is easier to handle than traditional CAPTCHAs but harder than simple JS challenges
- The
managedmode may require no user interaction, whileinteractivemode requires a click
For production scraping, ScraperAPI is the most reliable option as it maintains up-to-date Turnstile solving capabilities.