Cross-posted from blog.peak.fo. I work on Peak, the solving API used in the example below.
Before you can solve a Cloudflare Turnstile, you need its sitekey. It's a short public string, real ones start with 0x4, and it's sitting in the page's HTML in plain sight. Here's how to grab it in a few seconds, and what to do when the widget is loaded dynamically and doesn't show up in the raw source.
Quick answer: open the page, search the HTML for data-sitekey or 0x4, and copy the value. That's the sitekey. The rest of this covers the cases where it isn't that simple.
The fast way: read it off the widget
Turnstile renders into an element that carries the key as a data-sitekey attribute:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxxxxxxxxxx"></div>
In a browser, open DevTools, hit the elements panel, and search (Ctrl+F inside it) for cf-turnstile or data-sitekey. Copy the value. Done. If you'd rather stay in the terminal, curl the page and grep for the pattern:
curl -s https://target.com/ | grep -oE 'data-sitekey="[^"]+"' | head -1
When it's not in the raw HTML
Plenty of sites load Turnstile with JavaScript after the page renders, or configure it through the turnstile.render() call instead of a data attribute. Then a plain curl shows nothing, because the key only appears once scripts run. Two options.
Search the JavaScript. The key still has to reach the browser, so grep the page's scripts for sitekey or the 0x4AAAAAAA prefix. It's often passed as an argument to turnstile.render():
turnstile.render('#container', { sitekey: '0x4AAAAAAAxxxxxxxxxxxx' });
Or read it from a real browser. Load the page in a headless browser, wait for the widget, and pull the attribute:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
page = p.chromium.launch().new_page()
page.goto("https://target.com/")
page.wait_for_selector("[data-sitekey]")
sitekey = page.get_attribute("[data-sitekey]", "data-sitekey")
print(sitekey)
There's a third place to look. Once the widget has rendered, it loads a frame from challenges.cloudflare.com, and the sitekey is one of the path segments in that frame's URL, for example .../turnstile/f/av0/rch/fmb9f/1x00000000000000000000AA/light/fbE/new/normal. The iframe sits inside a closed shadow root, so querySelector won't find it, but the frame list will. In Playwright that's [f.url for f in page.frames]. In DevTools, expand the widget's #shadow-root and read the iframe src.
Test keys look different
Cloudflare publishes dummy sitekeys for development, and some demo pages use them. They don't start with 0x4, so a grep for that prefix will miss them:
| Sitekey | Behavior |
|---|---|
1x00000000000000000000AA |
always passes |
2x00000000000000000000AB |
always blocks |
3x00000000000000000000FF |
forces an interactive challenge |
I checked this against Cloudflare's own demo page (demo.turnstile.workers.dev): its widget uses 1x00000000000000000000AA. If you're pulling keys out of a page and get one of these, you're looking at a test widget, not a protected form.
The sitekey isn't a secret
Worth saying plainly: the sitekey is public by design. It's the client-side identifier the widget needs, so it's always exposed in the page. The private half (the secret key) lives on the site's server and never reaches you, and you don't need it. To solve the challenge you only need the public sitekey plus the page URL.
Once you have both, you're ready to solve. The full flow, with code and the token submission step, is in how to solve Cloudflare Turnstile, and the parameter reference is in the Peak docs.
Here's the whole thing with Peak's API, which is the one I work on. Send the sitekey and page URL, get a token back, and put it in the form's cf-turnstile-response field:
import requests
resp = requests.post(
"https://api.peak.fo/solve",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"task_type": "turnstiletask",
"url": "https://target.com/",
"sitekey": "0x4AAAAAAAxxxxxxxxxxxx",
},
timeout=60,
)
token = resp.json()["data"]["token"]
Pricing is $0.90 per 1,000 successful solves, dropping to $0.35 at the largest package, and failed solves aren't billed. New accounts get 1,000 free solves to test with.
FAQ
Where is the Turnstile sitekey?
In the page HTML, as the data-sitekey attribute on the widget element, or passed to turnstile.render() in the page's JavaScript. Production keys start with 0x4; Cloudflare's dummy test keys don't (see above).
Is the sitekey secret?
No. The sitekey is public and always visible in the page. The secret key is separate, stays on the site's server, and you never need it to solve the challenge.
Have the sitekey and URL? Start solving free at peak.fo.
Top comments (0)