Aller au contenu principal

HTTP 429 et backoff exponentiel

Gérer les dépassements de limites

Lorsque votre application dépasse les limites RPM ou TPM de votre tier, l’API Grok répond avec un code HTTP 429 (Too Many Requests). Ce n’est pas une erreur fatale : c’est un signal de contrôle de flux que votre application doit savoir gérer proprement. En production, un code 429 mal géré peut provoquer une cascade d’échecs dans toute votre chaîne de traitement.

Anatomie d’une réponse 429

Quand vous recevez un code 429, la réponse indique que vous avez temporairement dépassé votre quota. Votre application ne doit surtout pas renvoyer immédiatement la même requête : cela aggraverait la situation en consommant inutilement des appels supplémentaires.

La bonne approche consiste à implémenter un mécanisme de backoff exponentiel avec jitter (variation aléatoire).

Backoff exponentiel : le principe

Le backoff exponentiel est une stratégie de retry qui augmente progressivement le délai entre chaque tentative :

import time
import random

def appel_avec_retry(requete, max_retries=5):
    for tentative in range(max_retries):
        reponse = envoyer_requete(requete)

        if reponse.status_code != 429:
            return reponse

        delai_base = 2 ** tentative  # 1, 2, 4, 8, 16 secondes
        jitter = random.uniform(0, delai_base * 0.5)
        delai = delai_base + jitter

        time.sleep(delai)

    raise Exception("Limite de retries atteinte")

Pourquoi le jitter est essentiel

Sans jitter, si plusieurs clients reçoivent un 429 au même moment, ils retenteront tous exactement au même instant, provoquant un nouveau pic de charge (phénomène de “thundering herd”). Le jitter disperse les retries dans le temps et évite cette synchronisation involontaire.

Stratégies avancées pour la production

File d’attente avec rate limiter

Pour une application à fort volume, un simple retry ne suffit pas. Implémentez une file d’attente avec un rate limiter côté client :

import asyncio
from asyncio import Semaphore

# Limite à 25 requetes/seconde (marge sous 30 RPM/s)
semaphore = Semaphore(25)

async def requete_limitee(payload):
    async with semaphore:
        reponse = await envoyer_requete_async(payload)
        await asyncio.sleep(1.0 / 25)  # espacement minimal
        return reponse

Circuit breaker

En complément du backoff, un circuit breaker coupe temporairement les appels API après un nombre configurable d’échecs consécutifs. Cela protège à la fois votre application et l’API :

  • Fermé : les requêtes passent normalement
  • Ouvert : toutes les requêtes sont refusées localement pendant un délai configurable
  • Semi-ouvert : une requête test est envoyée pour vérifier si le service est rétabli

Erreurs à éviter

  • Retry immédiat sans délai : aggrave le problème et peut prolonger la durée du blocage
  • Retry infini : consomme des ressources inutilement et peut bloquer des threads
  • Ignorer le 429 : traiter la réponse comme une erreur définitive prive votre utilisateur d’un résultat qui arriverait quelques secondes plus tard
  • Délai fixe : un retry toutes les 2 secondes exactement provoque des pics de charge prévisibles

Points clés à retenir

  • Le code HTTP 429 signale un dépassement de limite RPM ou TPM
  • Le backoff exponentiel avec jitter est la stratégie recommandée par xAI
  • En production, complétez le backoff par un rate limiter côté client et un circuit breaker
  • Ne retentez jamais immédiatement : attendez au minimum 1 seconde avant le premier retry
  • Surveillez le taux de 429 dans vos métriques comme indicateur de sous-dimensionnement