Scraping Central is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

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:

  1. Collects browser signals (environment, behavior, device data)
  2. Sends signals to Cloudflare's edge for analysis
  3. Returns a cf-turnstile-response token if the check passes
  4. 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 managed mode may require no user interaction, while interactive mode requires a click

For production scraping, ScraperAPI is the most reliable option as it maintains up-to-date Turnstile solving capabilities.