Un 429 signifie qu'une limite a été atteinte. La question utile est : la limite de qui ? Votre propre serveur web, un proxy ou un CDN en amont, une API que vous appelez, ou l'application elle-même. Chacun appelle une solution différente, et les en-têtes de réponse disent en général laquelle.

Lisez d'abord la réponse

curl -sSI https://yourdomain.com/api/thing | grep -i -E 'retry-after|ratelimit|x-'

Retry-After vous dit combien de temps attendre. Les en-têtes RateLimit donnent le plafond et ce qu'il reste. Un 429 sans ces en-têtes vient en général de votre propre serveur, ou d'un proxy qu'on n'a pas configuré pour s'expliquer.

Si c'est votre propre Nginx

grep "limiting requests" /var/log/nginx/error.log | tail
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /api/ {
    limit_req zone=api burst=20 nodelay;
    limit_req_status 429;
}
Derrière un CDN ou un répartiteur de charge, $binary_remote_addr désigne le PROXY : tous les visiteurs partagent alors un seul seau, et une poignée d'entre eux déclenche la limite pour tout le monde. Configurez d'abord la vraie adresse du client — voir Cloudflare et la vraie IP du visiteur.

Si c'est vous qu'on limite

  • Respectez Retry-After. Réessayer aussitôt prolonge la plupart des blocages.
  • Reculez de façon exponentielle, avec un aléa, pour qu'une flotte de travailleurs ne réessaie pas au pas cadencé.
  • Mettez en cache ce que vous récupérez. La plupart des limites de débit sont atteintes en reposant sans cesse la même question.
  • Groupez quand l'API le permet — un appel pour cinquante enregistrements plutôt que cinquante appels.
for i in 1 2 3 4 5; do
  curl -fsS "$URL" && break
  sleep $(( (2 ** i) + RANDOM % 3 ))
done

S'il s'agit d'un point d'entrée de connexion

Alors il fonctionne comme prévu. Voir Limiter le débit d'un point d'entrée de connexion.