Corriger l'erreur 429 Trop de requêtes : Recul, Budgets et Répartition des IP

Un 429 est le seul blocage qui vous indique exactement comment le résoudre — si vous lisez les en-têtes de réponse au lieu de simplement réessayer. Voici l'arithmétique derrière un débit de scraping sécurisé.

HTTP 429 Trop de requêtes est le blocage le plus honnête qu'un site web puisse vous envoyer. Contrairement à un 403, il nomme le problème — vous êtes allé trop vite — et il fournit souvent le remède dans un en-tête de réponse. Pourtant, la réaction standard est d'envelopper l'appel dans une boucle de réessai et d'espérer, ce qui transforme un problème de rythme solvable en un désordre générateur de blocages. Ce guide traite le 429 pour ce qu'il est : un problème arithmétique avec quatre leviers. Lisez la réponse, respectez la limite publiée, reculez correctement lorsque vous dépassez, et répartissez la charge restante sur plusieurs identités.

Ce que signifie 429, et quand il ment

Un limiteur de débit compte les requêtes par identité — généralement une adresse IP, parfois une clé API ou un cookie de session — dans une fenêtre temporelle. Dépassez le seuil et vous obtenez 429 au lieu du contenu. Les implémentations diffèrent : les seaux de jetons distribuent une allocation fixe qui se remplit selon un calendrier, les fenêtres glissantes comptent sur une période continue plutôt qu'en minutes d'horloge, et les systèmes à niveaux limitent à une limite douce avant de bloquer durement à une limite supérieure. Celui auquel vous faites face détermine si une courte pause suffit ou si vous devez attendre toute une fenêtre.

Maintenant, la mise en garde qui sauve des heures : un 429 sur votre première requête n'est pas une limitation de débit. C'est une réponse de bot déguisée en limitation de débit. Un fil de discussion très lu sur Stack Overflow décrit exactement cela — le tout premier appel d'un scraper a renvoyé une page indiquant "Scraper de contenu malveillant Veuillez utiliser robots.txt Votre IP a été limitée" avec le 429. Rien n'avait été dépassé ; le serveur a simplement décidé que le client était un bot et a choisi ce code. Si vous voyez 429 avant d'avoir envoyé un quelconque volume, traitez-le comme un problème de détection et travaillez sur les en-têtes et les vérifications IP dans notre guide sur les erreurs 403 interdites à la place.

Lisez la réponse avant de modifier le code

Les serveurs bienveillants vous disent quand revenir. Retry-After contient soit un nombre de secondes, soit une date HTTP. De nombreuses API ajoutent X-RateLimit-Limit (le plafond), X-RateLimit-Remaining (ce qui reste dans la fenêtre actuelle) et X-RateLimit-Reset (quand elle se remplit). Ces trois éléments transforment le réessai réactif en un rythme proactif : vous pouvez ralentir avant le blocage plutôt qu'après.

import requests

r = requests.get("https://target.example/api/items", timeout=20)
print(r.status_code)
for h in ("Retry-After", "X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset"):
    if h in r.headers:
        print(f"{h}: {r.headers[h]}")

# No headers at all? The limit is undocumented - measure it:
# send a slow ramp (1 req/s, then 2, then 4) and note where 429 starts.

Si rien d'utile ne revient, mesurez la limite vous-même avec une rampe : exécutez une requête par seconde pendant une minute, puis deux, puis quatre, et enregistrez le taux auquel les 429 apparaissent. Dix minutes de mesure valent mieux qu'une semaine de suppositions, et le nombre que vous trouvez devient le budget sur lequel tout le reste est construit.

Diagramme de bandes montrant les écarts de requêtes sûrs, marginaux et déclencheurs de 429 par rapport à une limite de 100 requêtes par minute
Le calcul complet : 60 secondes divisées par la limite publiée, puis ajoutez une marge pour le jitter réseau.

L'arithmétique du rythme

Prenez la limite documentée et divisez. Un plafond de 100 requêtes par minute signifie 60 / 100 = 0,6 secondes entre les requêtes comme plancher absolu — et un plancher n'est pas un objectif. La latence réseau varie, votre horloge et celle du serveur ne s'accordent pas, et une rafale à la limite de la fenêtre peut doubler votre taux apparent. Visez 70-80% de la limite : environ 0,8 secondes par requête dans cet exemple, ce qui vous donne encore 75 pages par minute.

La concurrence suit le même nombre. Si vous voulez 1,25 requêtes par seconde et que chaque requête prend 2 secondes aller-retour, vous avez besoin de 1,25 x 2 = 2,5 requêtes en vol — donc un sémaphore de 3, pas les 50 par défaut de votre code asynchrone. L'éventail asynchrone non limité est la cause la plus courante de 429 : une centaine de coroutines lancées en même temps arrivent comme une rafale instantanée, peu importe à quel point la moyenne semble polie. Si vous scrapez avec asyncio, les motifs de sémaphore dans le scraping Python asynchrone avec httpx et aiohttp sont la solution.

Recul qui fonctionne : exponentiel, plafonné, avec jitter

Lorsque vous rencontrez un 429, respectez Retry-After s'il est présent. Sinon, commencez à une seconde et doublez — 1, 2, 4, 8, 16 — jusqu'à un plafond dur pour qu'une cible cassée ne bloque pas votre file d'attente indéfiniment. Ensuite, ajoutez du jitter. Sans randomisation, chaque travailleur qui a heurté le mur au même moment réessaye au même moment, reproduisant la rafale qui a causé le problème.

import random, time, requests

def get_with_backoff(session, url, max_tries=6, cap=120.0):
    for attempt in range(max_tries):
        r = session.get(url, timeout=20)
        if r.status_code != 429:
            return r

        ra = r.headers.get("Retry-After", "")
        wait = float(ra) if ra.isdigit() else 2.0 ** attempt   # 1, 2, 4, 8, 16, 32
        wait = min(wait, cap)
        wait += random.uniform(0, wait * 0.3)                   # jitter: break the lockstep

        time.sleep(wait)
    raise RuntimeError(f"still 429 after {max_tries} attempts: {url}")

Mieux encore, fermez la boucle. L'augmentation additive/diminution multiplicative vous donne un scraper qui trouve la limite par lui-même et reste juste en dessous : augmentez le taux tant que les réponses sont propres, divisez-le par deux dès qu'un 429 arrive. Gardez un régulateur par domaine — les limites sont par hôte, et une cible agressive ne devrait pas ralentir les quarante autres.

class Pacer:
    """One per domain. Additive increase, multiplicative decrease."""
    def __init__(self, rps=2.0, floor=0.2, ceiling=8.0):
        self.rps, self.floor, self.ceiling = rps, floor, ceiling

    def ok(self):          # clean response: creep faster
        self.rps = min(self.ceiling, self.rps + 0.05)

    def throttled(self):   # 429: halve immediately
        self.rps = max(self.floor, self.rps / 2)

    @property
    def gap(self):
        return 1.0 / self.rps

Répartition de la charge : le budget est par identité, pas par projet

Une fois que vous avez un rythme correct et que vous avez encore besoin de plus de débit, le seul levier restant est les identités. Parce que le compteur est lié à votre IP, N sorties IP vous donnent N fois le budget — l'arithmétique est aussi simple que cela. Si un site tolère 60 requêtes par minute par adresse et que vous avez besoin de 1 200 pages par minute, cela signifie 20 sorties concurrentes fonctionnant confortablement en dessous du plafond, pas une sortie fonctionnant vingt fois au-dessus.

C'est pour cela que les proxies rotatifs existent réellement. Une passerelle rotative attribue à chaque requête une IP résidentielle différente à partir d'un pool de plus de 90 millions d'adresses dans plus de 200 pays, de sorte que les compteurs par IP ne se remplissent jamais. Deux règles font la différence entre répartir la charge et brûler un pool : gardez le taux par IP en dessous de la limite même après la rotation (la rotation multiplie votre budget, elle ne le supprime pas), et utilisez des sessions persistantes pour tout flux qui s'étend sur plusieurs requêtes — une connexion, un panier, un ensemble de résultats paginés — afin que la session ne se casse pas en cours de route. Lorsque vous avez besoin de la même IP pendant quelques minutes et d'une nouvelle ensuite, les compromis sont expliqués dans sessions persistantes versus sessions rotatives.

Multipliez votre budget de taux avec des IP résidentielles

Panneau de statistiques montrant les chiffres clés pour éviter les erreurs HTTP 429 : intervalle minimum de 600 ms, recul exponentiel, réduction de taux de 50% et budgets par IP
Quatre chiffres dirigent tout le système. Dérivez-les des limites de la cible, jamais de l'optimisme.

Moins cher que plus d'IP : envoyez moins de requêtes

La discipline de la bande passante paie deux fois — moins de requêtes signifie moins de 429 et une facture plus petite, ce qui est le même argument que nous faisons dans réduire les coûts de bande passante des proxies. Et si vous préférez ne pas construire d'infrastructure de rythme du tout, l'API Scraper de QuantumProxies absorbe les réessais, la rotation et la limitation par domaine derrière un seul point de terminaison qui renvoie du markdown, JSON ou HTML.

Questions fréquemment posées

Comment éviter l'erreur HTTP 429 trop de requêtes en Python ?

Définissez un intervalle délibéré entre les requêtes basé sur la limite publiée de la cible, limitez la concurrence avec un sémaphore dimensionné au taux multiplié par la latence, respectez Retry-After lorsqu'il apparaît, et réessayez avec un recul exponentiel plus du jitter. Si vous avez besoin de plus de débit après cela, répartissez les requêtes sur des IP de proxy rotatives plutôt que de raccourcir l'intervalle.

Combien de temps dois-je attendre après un 429 ?

Exactement aussi longtemps que Retry-After le dit, si le serveur l'envoie — cela peut être un compte de secondes ou une date HTTP. Sans cet en-tête, commencez à une seconde et doublez à chaque 429 suivant jusqu'à un plafond d'une minute ou deux, en ajoutant du jitter aléatoire pour que les travailleurs parallèles ne réessayent pas à l'unisson.

Les proxies corrigent-ils les erreurs 429 ?

Ils multiplient votre budget, ils ne suppriment pas la limite. Parce que les compteurs sont liés à l'IP du client, répartir une exécution sur de nombreuses sorties résidentielles maintient chaque adresse sous le seuil. Mais un pool martelé à dix fois la limite par IP collectera toujours des 429 — et brûlera sa réputation. Rythmez d'abord, puis faites tourner.

Un 429 est-il la même chose qu'une interdiction ?

Non. Un 429 est temporaire par conception et se réinitialise lorsque la fenêtre se réinitialise, ce qui le distingue d'un blocage d'identité 403. L'ignorer à plusieurs reprises est la façon dont il devient permanent : un dépassement soutenu est exactement le signal qui transforme un étranglement en une interdiction d'IP plus longue.

Pourquoi ai-je un 429 dès la première requête ?

Parce que rien n'a été réellement compté. Certains serveurs renvoient 429 à tout client qu'ils considèrent comme un bot, indépendamment du volume — le code de statut est juste leur réponse choisie. Vérifiez le corps de la réponse : s'il mentionne robots.txt, des scrapers ou un pare-feu, corrigez vos en-têtes, l'empreinte TLS et l'IP de sortie plutôt que votre rythme.

Traitez les limites de débit comme un budget que vous dépensez délibérément. Mesurez le plafond, fonctionnez à 70-80% de celui-ci, reculez avec du jitter lorsque vous dépassez, et achetez plus d'identités seulement une fois que le rythme est correct. Fait dans cet ordre, les 429 cessent d'être une classe d'erreur et deviennent un nombre dans un fichier de configuration.

Obtenez des proxies rotatifs et arrêtez de lutter contre les limites de débit