Cloudflare Turnstile est une alternative aux CAPTCHAs centrée sur la confidentialité, qui s'exécute silencieusement en arrière-plan. Contrairement aux CAPTCHAs classiques, il affiche rarement un défi visible : il collecte des signaux du navigateur et émet un token vérifié par le backend du site.
Ce guide explique comment résoudre Turnstile par programmation avec l'API CaptchaAI. Si vous n'avez pas encore lu le Quickstart CaptchaAI, commencez par là pour comprendre le flux général en 4 étapes.
Prérequis
| Élément | Valeur |
|---|---|
| Clé API CaptchaAI | Depuis le tableau de bord sur captchaai.com |
| Sitekey Turnstile | Extrait de la page (commence par 0x) |
| URL de la page | URL complète où apparaît Turnstile |
| Langage | Python 3.7+ ou Node.js 14+ |
Étape 1 : trouvez le sitekey Turnstile
Le sitekey se trouve généralement dans le HTML, dans une balise div ou script :
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
Ou rendu par JavaScript :
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
Trois façons de l'extraire :
- DevTools du navigateur — onglet Elements, recherchez
data-sitekeyoucf-turnstile. - Code source —
Ctrl+U, puis recherchez les chaînes commençant par0x. - Onglet Network — filtrez sur
challenges.cloudflare.com; le sitekey apparaît dans les paramètres de requête.
Le sitekey Turnstile commence toujours par
0xet fait généralement 22 caractères. C'est ce qui le distingue des clés reCAPTCHA, qui commencent par6L.
Étape 2 : envoyez la tâche
POST vers https://ocr.captchaai.com/in.php avec method=turnstile :
import requests
API_KEY = "YOUR_CAPTCHAAI_KEY"
SITEKEY = "0x4AAAAAAAC3DHQFLr1GavNl"
PAGEURL = "https://example.com/login"
r = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": SITEKEY,
"pageurl": PAGEURL,
"json": 1,
})
data = r.json()
if data["status"] != 1:
raise RuntimeError(f"submit failed: {data}")
task_id = data["request"]
print("task id:", task_id)
Équivalent Node.js :
const axios = require("axios");
const { data } = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: process.env.CAPTCHAAI_KEY,
method: "turnstile",
sitekey: "0x4AAAAAAAC3DHQFLr1GavNl",
pageurl: "https://example.com/login",
json: 1,
},
});
if (data.status !== 1) throw new Error(`submit failed: ${JSON.stringify(data)}`);
const taskId = data.request;
Réponse attendue : {"status": 1, "request": "<task_id>"}. Conservez task_id pour le polling.
Étape 3 : interrogez le résultat
Turnstile se résout en général en 10 à 25 secondes. Attendez 10 secondes puis interrogez toutes les 5 secondes, 40 itérations maximum :
import time
time.sleep(10)
for _ in range(40):
r = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
res = r.json()
if res["status"] == 1:
token = res["request"]
break
if res["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"solver error: {res}")
time.sleep(5)
else:
raise TimeoutError("turnstile solving timed out")
print("token (60 premiers caractères) :", token[:60])
Le token retourné est une chaîne Base64 qui commence en général par 0. et fait 400 à 600 caractères.
Étape 4 : injectez le token dans la page
Une fois le token obtenu, placez-le dans le champ caché cf-turnstile-response du formulaire Turnstile, puis soumettez le formulaire.
Selenium :
driver.execute_script(
"document.querySelector('[name=cf-turnstile-response]').value = arguments[0];",
token,
)
driver.find_element("css selector", "form").submit()
Playwright :
page.evaluate(
"(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
token,
)
page.click("button[type=submit]")
HTTP brut : ajoutez cf-turnstile-response=<token> au corps application/x-www-form-urlencoded.
Le token Turnstile est valide environ 120–300 secondes. Utilisez-le immédiatement, sans quoi le backend renverra
timeout-or-duplicate.
Exemple Python complet
import os, time, requests
API = "https://ocr.captchaai.com"
KEY = os.environ["CAPTCHAAI_KEY"]
def solve_turnstile(sitekey: str, pageurl: str) -> str:
r = requests.post(f"{API}/in.php", data={
"key": KEY, "method": "turnstile",
"sitekey": sitekey, "pageurl": pageurl, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] != 1:
raise RuntimeError(f"submit: {j}")
tid = j["request"]
time.sleep(10)
for _ in range(40):
r = requests.get(f"{API}/res.php", params={
"key": KEY, "action": "get", "id": tid, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] == 1:
return j["request"]
if j["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"poll: {j}")
time.sleep(5)
raise TimeoutError("timeout")
if __name__ == "__main__":
print(solve_turnstile("0x4AAAAAAAC3DHQFLr1GavNl", "https://example.com/login"))
Erreurs fréquentes
| Code | Signification | Action |
|---|---|---|
ERROR_WRONG_USER_KEY |
Format de clé API invalide | Vérifiez que CAPTCHAAI_KEY est complet |
ERROR_KEY_DOES_NOT_EXIST |
Clé API introuvable | Recopiez la clé depuis le tableau de bord |
ERROR_ZERO_BALANCE |
Solde nul | Rechargez et réessayez |
ERROR_PAGEURL |
Paramètre pageurl manquant | Envoyez l'URL complète avec https:// |
ERROR_CAPTCHA_UNSOLVABLE |
Échec après plusieurs tentatives | Vérifiez la cohérence sitekey/pageurl, réessayez une fois |
Plus de détails dans le guide reCAPTCHA v2.
Quand cela ne fonctionne pas
- Sitekey dynamique. Certains sites Cloudflare émettent un nouveau sitekey à chaque visite ; refaites le scraping avant chaque tâche.
- pageurl exact. Le backend Turnstile compare l'URL strictement : envoyez le chemin exact, sans la query string.
- Empreinte TLS. Cloudflare peut rejeter un client selon sa signature TLS. Utilisez
curl_cffi, Playwright ou un vrai navigateur. - Token expiré. Utilisez-le en moins de 2 minutes ou il faudra résoudre à nouveau.
- Qualité du proxy. Les IP datacenter bon marché déclenchent souvent des défis supplémentaires. Préférez des proxies résidentiels ou mobiles.