Troubleshooting

Calendrier d’expiration du jeton Cloudflare Turnstile et conditions de course

Combien de temps un jeton Cloudflare Turnstile reste-t-il valable ? Environ 300 secondes après sa génération.

Largement assez pour un formulaire simple, mais pas pour un parcours en plusieurs étapes ou un traitement par lots. Passé ce délai, le site rejette le jeton même si votre script marchait en local. Voici comment repérer le problème et corriger le timing.

En bref :

  • Le jeton Turnstile vit ~300 secondes, chronométrées par Cloudflare.
  • Formulaires multi-étapes et traitement par lots sont les causes les plus fréquentes d’expiration.
  • La correction tient en une règle : résoudre juste à temps, jamais à l’avance.

Combien de temps un jeton Turnstile reste-t-il valable ?

Les jetons Turnstile expirent environ 300 secondes (5 minutes) après leur création — plus généreux que d’autres types, mais les conditions de course apparaissent quand même dans les workflows à plusieurs étapes. Pour comparer :

  • reCAPTCHA v2/v3 : ~120 secondes
  • Cloudflare Turnstile : ~300 secondes
  • hCaptcha : ~120 secondes

Le chronomètre démarre quand Cloudflare génère le jeton, pas quand CaptchaAI vous le renvoie. C’est la confusion la plus fréquente derrière les échecs « aléatoires » en production.

La fenêtre de tir entre résolution et soumission du jeton

Un exemple concret, minute par minute :

Time 0:00  — You submit a Turnstile task to CaptchaAI
Time 0:15  — CaptchaAI begins solving
Time 0:20  — Token is generated (timer starts here)
Time 0:25  — CaptchaAI returns token to you
Time 0:25+ — Your code processes the token
Time ???   — Your code submits the token to the site

L’horloge tourne dès 0:20. Vous avez ~5 minutes 20 pour soumettre — confortable sur le papier, moins en vrai parcours utilisateur :

Time 0:20  — Token generated
Time 0:25  — Received by your code
Time 0:30  — Fill form fields
Time 0:35  — Navigate to next page
Time 1:00  — Handle additional dialogs
Time 2:00  — Wait for page load
Time 4:00  — Network latency spike
Time 5:30  — Submit token → EXPIRED

Sur un worker OVHcloud ou en région AWS eu-west-3 (Paris), un pic de latence suffit à transformer cette marge en soumission ratée — mesurez l’âge réel du jeton, pas le temps de résolution annoncé.

Où les conditions de course se cachent le plus souvent

1. Formulaires en plusieurs étapes

Les parcours à plusieurs pages sont les premiers concernés :

Step 1: Fill personal info → Step 2: Fill address → 
Step 3: Solve CAPTCHA → Step 4: Review → Step 5: Submit

Si le CAPTCHA est résolu à l’étape 3 mais soumis à l’étape 5, le délai dépasse facilement les 5 minutes.

2. Files d’attente de traitement par lots

Résoudre tous les jetons à l’avance puis les consommer un par un : l’erreur classique des scripts à fort volume :

# DON'T: Solve all tokens first, then use them
tokens = []
for url in urls:
    tokens.append(solve_turnstile(url))  # Tokens age while waiting

for url, token in zip(urls, tokens):
    submit_form(url, token)  # Early tokens may be expired

3. Nouvelles tentatives avec un ancien jeton

Réutiliser un jeton après un échec de soumission casse deux fois :

  • Le jeton a peut-être expiré entre-temps.
  • Ou il est déjà consommé côté Cloudflare, même si la première soumission a échoué.
token = solve_turnstile(site_key, page_url)

for attempt in range(3):
    result = submit_form(page_url, token)
    if result.ok:
        break
    # BUG: Retrying with the same token — it may be expired OR already consumed

Trois stratégies pour ne jamais laisser un jeton expirer

Trois leviers, du plus simple au plus robuste :

  1. Résoudre juste à temps.
  2. Suivre l’âge du jeton.
  3. Repartir de zéro à chaque tentative.

Stratégie 1 : résoudre juste à temps

Demandez le jeton seulement quand vous êtes prêt à le soumettre — remplissez le formulaire d’abord, résolvez le CAPTCHA en dernier :

import requests
import time

def solve_turnstile(site_key, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "turnstile",
        "sitekey": site_key,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

# Complete all form steps FIRST
fill_personal_info()
fill_address()
navigate_to_review()

# THEN solve and submit immediately
token = solve_turnstile(site_key, page_url)
submit_form(token)  # Submit within seconds of receiving the token

Stratégie 2 : suivre l’âge du jeton

Encapsulez le jeton dans un objet qui connaît son âge, avec une marge de sécurité sous les 300 secondes réelles :

import time

class TimedToken:
    def __init__(self, token, created_at=None):
        self.token = token
        self.created_at = created_at or time.time()
        self.max_age = 270  # 4.5 min — safety margin from 5 min limit

    @property
    def is_valid(self):
        return (time.time() - self.created_at) < self.max_age

    @property
    def remaining_seconds(self):
        return max(0, self.max_age - (time.time() - self.created_at))

# Usage
timed_token = TimedToken(solve_turnstile(site_key, page_url))

# Check before using
if timed_token.is_valid:
    submit_form(timed_token.token)
else:
    # Solve a fresh token
    timed_token = TimedToken(solve_turnstile(site_key, page_url))
    submit_form(timed_token.token)

Stratégie 3 : jeton neuf à chaque nouvelle tentative (JavaScript)

Ne réessayez jamais avec le même jeton. Résolvez-en un nouveau à chaque tentative :

async function submitWithFreshToken(siteKey, pageUrl, formData) {
  const maxRetries = 3;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    // Always solve a fresh token for each attempt
    const token = await solveTurnstile(siteKey, pageUrl);

    const response = await fetch(pageUrl, {
      method: 'POST',
      body: JSON.stringify({ ...formData, 'cf-turnstile-response': token }),
      headers: { 'Content-Type': 'application/json' }
    });

    if (response.ok) return await response.json();

    console.log(`Attempt ${attempt + 1} failed, solving fresh token...`);
  }

  throw new Error('All attempts failed');
}

Repérer un jeton expiré avant qu’il casse votre parcours

Le site ne dit presque jamais « jeton expiré ». Surveillez plutôt ces signaux :

  • HTTP 403 après soumission → jeton invalide ou expiré.
  • Retour à la page du formulaire → vérification échouée.
  • Message « Échec de la vérification » → souvent une expiration.
  • Page de défi qui réapparaît → jeton rejeté.

Astuce : loggez l’âge du jeton avant d’ouvrir un ticket — neuf fois sur dix, il dépasse déjà 270 s.

Journaliser pour diagnostiquer plus vite

Consignez l’âge du jeton à chaque soumission — le moyen le plus rapide de confirmer l’expiration :

import time
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("turnstile")

token_received_at = time.time()
token = solve_turnstile(site_key, page_url)
logger.info(f"Token received, length: {len(token)}")

# ... workflow steps ...

submit_time = time.time()
age = submit_time - token_received_at
logger.info(f"Submitting token, age: {age:.1f}s")

if age > 270:
    logger.warning(f"Token may be expired (age: {age:.1f}s > 270s safety limit)")

Rafraîchissement automatique : navigateur vs API

Dans un flux navigateur, les widgets Turnstile actualisent automatiquement le jeton avant expiration. Le callback expired-callback se déclenche quand un jeton expire :

turnstile.render('#captcha', {
  sitekey: '0x4AAAA...',
  callback: (token) => {
    console.log('New token:', token);
  },
  'expired-callback': () => {
    console.log('Token expired — widget will auto-refresh');
  }
});

En API pure, sans navigateur, il n’y a aucun rafraîchissement automatique : la fraîcheur du jeton est entièrement sous votre responsabilité.

Tableau de dépannage express

Problème Cause Correctif
OK en test, échoue en production Flux prod plus lent Résoudre juste à temps
1er envoi OK, échecs ensuite Jeton déjà consommé Nouveau jeton par tentative
Échecs sur formulaires longs Expiration en cours de flux Résoudre en dernière étape
Batch peu fiable Jetons expirés avant usage Résoudre à la demande

FAQ

Peut-on prolonger la validité d’un jeton Turnstile ?

Non. L’expiration est fixée par Cloudflare, pas configurable. Résolvez un nouveau jeton quand l’ancien approche de sa limite.

Le compteur des 300 secondes démarre-t-il à la réception du jeton ou à sa génération ?

À sa génération, côté Cloudflare — pas quand CaptchaAI vous le renvoie. D’où la marge de 270 secondes recommandée plus haut.

Le rafraîchissement automatique du widget fonctionne-t-il aussi en API pure ?

Non. expired-callback n’existe que pour les widgets navigateur. En API (Python, Node.js, headless), suivez vous-même l’âge du jeton.

Faut-il résoudre les jetons à l’avance pour gagner du temps ?

Seulement si le workflow les consomme en quelques minutes. Pour du batch, résolvez à la demande — cause la plus fréquente d’échecs en file d’attente.

Articles connexes

Prochaines étapes

Récupérez votre clé API CaptchaAI et passez à une résolution juste à temps.

Les commentaires sont désactivés pour cet article.