How to Bypass Cloudflare Turnstile in Python: Playwright & 2Captcha API Guide
Cloudflare Turnstile has become the dominant alternative to legacy puzzle captchas. This tutorial breaks down how Turnstile works under the hood and provides production-grade Python scripts using Playwright and the 2Captcha API.
Why 2Captcha Ranks #1 for Cloudflare Turnstile
In our standardized telemetry benchmarks, 2Captcha proved the most consistent Turnstile solver, solving challenges in an average of 2.98 seconds at $0.50 per 1,000 solves with a 99.8% resolution rate. It natively supports optional parameters (action, cData, and pagedata) required by hardened Turnstile implementations.
Understanding How Cloudflare Turnstile Works
Unlike legacy image puzzles that prompt users to select traffic lights, Cloudflare Turnstile executes client-side cryptographic challenges within an embedded iframe. The browser sends device fingerprints, execution timing metrics, and proof-of-work calculations back to Cloudflare's edge nodes (challenges.cloudflare.com).
When the challenge verifies successfully, Turnstile generates an ephemeral verification token (typically 300+ characters) and writes it into a hidden form input named cf-turnstile-response. When the user submits the form, the backend server validates this token with Cloudflare's siteverify API.
Critical Turnstile Parameters
- Sitekey: Public identifier for the Turnstile widget (e.g.,
0x4AAAAAAADnPIDROrmt1Wwj). Always visible in the page source. - Page URL: The exact origin and path where the widget is embedded.
- Action (Optional): A contextual tag (e.g.,
login,checkout) defined by the website owner to segment traffic. - cData (Optional): Custom customer payload passed to the widget for server-side correlation.
Step 1: Install Dependencies
Install the official 2Captcha Python SDK and Microsoft Playwright:
pip install 2captcha-python playwright playwright install chromium
Step 2: Basic Turnstile API Call in Python
Before integrating into full browser automation, test your 2Captcha API connection directly. The SDK manages polling, task dispatching, and response decoding automatically:
import os
from twocaptcha import TwoCaptcha
api_key = os.getenv("TWOCAPTCHA_API_KEY")
solver = TwoCaptcha(api_key)
try:
print("Sending Turnstile task to 2Captcha...")
result = solver.turnstile(
sitekey="0x4AAAAAAADnPIDROrmt1Wwj",
url="https://target-website.com/login",
action="login", # Optional: captured from data-action
data="custom-cdata-val" # Optional: captured from data-cdata
)
token = result["code"]
print("Turnstile solved successfully!")
print(f"Token: {token[:45]}...")
except Exception as e:
print(f"Turnstile solving error: {e}")Step 3: Complete Playwright Automation Script
In production scraping or headless form submission, you must extract the parameters dynamically from the live DOM, request the token from 2Captcha asynchronously, inject it into the hidden input, and trigger any attached callback functions.
Here is the end-to-end Python script:
import asyncio
import os
from playwright.async_api import async_playwright
from twocaptcha import TwoCaptcha
API_KEY = os.getenv("TWOCAPTCHA_API_KEY")
solver = TwoCaptcha(API_KEY)
TARGET_URL = "https://peet.ws/turnstile-test"
async def bypass_turnstile():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
context = await browser.new_context()
page = await context.new_page()
print(f"Navigating to {TARGET_URL}...")
await page.goto(TARGET_URL, wait_until="domcontentloaded")
# 1. Locate the Turnstile container and extract sitekey
turnstile_elem = await page.wait_for_selector(".cf-turnstile, [data-sitekey]", timeout=15000)
sitekey = await turnstile_elem.get_attribute("data-sitekey")
action = await turnstile_elem.get_attribute("data-action") or ""
cdata = await turnstile_elem.get_attribute("data-cdata") or ""
print(f"Found Turnstile: sitekey={sitekey}, action={action}")
# 2. Request solution from 2Captcha in a separate worker thread
loop = asyncio.get_running_loop()
solution = await loop.run_in_executor(
None,
lambda: solver.turnstile(
sitekey=sitekey,
url=page.url,
action=action if action else None,
data=cdata if cdata else None
)
)
token = solution["code"]
print(f"Received solution token from 2Captcha in {solution.get('solve_time', 'N/A')}s")
# 3. Inject token into cf-turnstile-response and fire callback
await page.evaluate("""(token) => {
// Populate all matching hidden inputs
const inputs = document.querySelectorAll('input[name="cf-turnstile-response"]');
inputs.forEach(input => {
input.value = token;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
});
// If a global callback is registered, trigger it
if (window.turnstileCallback && typeof window.turnstileCallback === 'function') {
window.turnstileCallback(token);
}
}""", token)
# 4. Submit form or verify challenge resolution
submit_btn = await page.query_selector('button[type="submit"], input[type="submit"]')
if submit_btn:
await submit_btn.click()
print("Form submitted successfully with Turnstile token!")
await page.wait_for_timeout(5000)
await browser.close()
if __name__ == "__main__":
asyncio.run(bypass_turnstile())Troubleshooting Common Turnstile Traps
1. Token Expired or Invalid
Cloudflare Turnstile tokens have a strict 300-second (5 minute) TTL. Ensure your script injects and submits the token immediately after 2Captcha returns it.
2. Action or cData Mismatch
If the target site uses data-action or data-cdata on the widget element and you omit them in the API request, the site's backend validation will reject the token even if 2Captcha solved it successfully. Always extract both attributes if present.
3. Interstitial 5-Second Challenge Pages
When Cloudflare presents a full-page "Checking your browser before accessing..." interstitial, you need a cf_clearance cookie rather than a form submission token. 2Captcha provides the Turnstile Clearance Task type specifically for interstitial mode.
Advanced Evasion: TLS & IP Fingerprints Matter
Cloudflare Turnstile doesn't just inspect DOM interactions—it audits your TLS ClientHello handshake (JA4 fingerprint) and client IP reputation score before deciding whether to trigger interactive challenges.
- Test your automation stack's JA4 and Akamai H2 signatures on 2-Proxy.com/tls-fingerprint ↗.
- Need clean rotating residential IPs to prevent rate-limit blocks? Browse benchmarks on 2-Proxy.com ↗ or ScrapingReview.com ↗.
- Looking to avoid local browser infrastructure altogether? Compare managed cloud browser APIs on Browser-Review.com ↗.