Un 429 significa que se alcanzó un límite. La pregunta útil es de quién: de tu propio servidor web, de un proxy o CDN por delante, de una API que estás llamando, o de la propia aplicación. Cada caso tiene una solución distinta, y las cabeceras de la respuesta suelen decir cuál.

Lee primero la respuesta

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

Retry-After te dice cuánto esperar. Las cabeceras RateLimit indican el techo y lo que queda. Un 429 sin esas cabeceras suele venir de tu propio servidor o de un proxy al que no se configuró para explicarse.

Si es tu propio 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;
}
Detrás de una CDN o un balanceador, $binary_remote_addr es el PROXY, así que todos los visitantes comparten un mismo cubo y un puñado de ellos dispara el límite para todo el mundo. Configura primero la dirección real del cliente; consulta Cloudflare y la IP real del visitante.

Si el limitado eres tú

  • Respeta Retry-After. Reintentar de inmediato alarga casi todos los bloqueos.
  • Retrocede de forma exponencial y con algo de azar, para que una flota de procesos no reintente al unísono.
  • Guarda en caché lo que descargas. Casi todos los límites de tasa se alcanzan repitiendo la misma pregunta.
  • Agrupa donde la API lo permita: una llamada para cincuenta registros en vez de cincuenta llamadas.
for i in 1 2 3 4 5; do
  curl -fsS "$URL" && break
  sleep $(( (2 ** i) + RANDOM % 3 ))
done

Si es un punto de acceso de inicio de sesión

Entonces funciona como debe. Consulta Limitar la tasa en un punto de inicio de sesión.