Por qué implementar rate limiting
Cada API tiene una capacidad finita y no todos los clientes se comportan correctamente. Un scraper, un cliente con errores atrapado en un bucle de reintentos o un pico de tráfico legítimo pueden agotar las conexiones de tu base de datos y dejar el servicio fuera de línea para todos. El rate limiting limita la velocidad a la que un cliente puede realizar solicitudes para que ningún usuario individual pueda consumir todos los recursos del sistema.
También hace que los costos sean predecibles, garantiza el uso justo entre tenants y planes, y te proporciona una herramienta para frenar el abuso antes de que se convierta en una caída del servicio.
Qué limitar
Un límite solo tiene sentido en relación con una identidad. Algunas opciones comunes son:
- API key — la mejor opción para comunicaciones server-to-server y clientes de terceros.
- User id — lo más natural para aplicaciones con autenticación.
- IP address — una alternativa para el tráfico anónimo, aunque muchos usuarios pueden compartir la misma.
- Combinación — clave más IP, o usuario más endpoint, para mayor precisión.
- Endpoint o tier — límites más estrictos para operaciones costosas, como búsquedas o exportaciones.
También debes decidir el alcance: un límite global, un límite por endpoint, o ambos. Un límite global protege el servicio; los límites por endpoint protegen tareas específicas que consumen muchos recursos.
Algoritmos
Cuatro algoritmos cubren casi cualquier necesidad.
Fixed window — cuenta las solicitudes en un intervalo fijo y se reinicia al llegar al límite.
limit: 100 per minute
key: client:123:2026-09-15T10:05
Es económico y sencillo, pero un cliente puede enviar 100 solicitudes al final de una ventana y otras 100 al inicio de la siguiente, duplicando efectivamente la tasa en el límite.
Sliding window — cuenta durante los últimos N segundos en lugar de un bloque fijo, generalmente combinando la ventana actual y la anterior mediante un promedio ponderado. Es más fluido que el fixed window con un coste similar.
Token bucket — un cubo se rellena a un ritmo constante y cada solicitud consume un token. Permite ráfagas cortas hasta el tamaño del cubo mientras mantiene una tasa promedio, lo cual coincide con el comportamiento de los clientes reales.
// token-bucket.js
const capacity = 20;
const refillPerSecond = 5;
let tokens = capacity;
let last = Date.now();
function allow() {
const now = Date.now();
tokens = Math.min(capacity, tokens + ((now - last) / 1000) * refillPerSecond);
last = now;
if (tokens < 1) return false;
tokens -= 1;
return true;
}
Leaky bucket — las solicitudes entran en una cola que se vacía a un ritmo fijo. Suaviza el tráfico para obtener una salida constante, lo cual es útil cuando los sistemas downstream necesitan una carga estable.
Cómo responder a los límites
Informa a los clientes sobre lo que está sucediendo mediante señales estándar.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
- 429 Too Many Requests es el código de estado correcto.
- Retry-After indica al cliente cuándo intentar de nuevo, ya sea en segundos o con una fecha.
- Los encabezados RateLimit-* exponen el límite, el recuento restante y el tiempo de reinicio para que los clientes puedan regular su ritmo.
Devolver 200 o descartar solicitudes silenciosamente oculta el problema y provoca errores misteriosos en el cliente. Los clientes bien implementados leen Retry-After y reducen la frecuencia de sus peticiones automáticamente.
Limitación distribuida
Si ejecutas más de una instancia, los contadores deben compartirse. Los límites en memoria se aplican por instancia, por lo que con diez servidores, un cliente obtiene efectivamente diez veces el límite.
// redis.js
const key = `ratelimit:${apiKey}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60);
if (count > 100) {
// reject with 429
}
Utiliza operaciones atómicas, o una librería que use un script de Lua, para que los incrementos y los tiempos de expiración estén libres de condiciones de carrera (race-free). Redis es el almacenamiento habitual porque es rápido y soporta scripts atómicos y expiración. Los API gateways y las plataformas edge pueden aplicar los límites antes incluso de que el tráfico llegue a tu aplicación, que es el lugar más eficiente para hacerlo.
Cuotas frente a límites de tasa (rate limits)
Resuelven problemas diferentes y a menudo coexisten.
- Rate limit — qué tan rápido: 100 solicitudes por minuto.
- Cuota — cuánto en total: 100,000 solicitudes por mes, vinculadas a un plan.
Un cliente puede mantenerse por debajo de su rate limit durante todo el mes y aun así agotar su cuota. Rastrea las cuotas por separado, generalmente con un contador de mayor duración, y devuelve un error específico cuando una cuota se haya agotado para que los clientes conozcan la diferencia.
Evitar afectar a los usuarios reales
Los límites deben detener el abuso sin penalizar el uso normal.
- Establece los límites basándote en el tráfico medido, dejando un margen para picos de demanda.
- Permite ráfagas de tráfico mediante un token bucket en lugar de un corte abrupto.
- Utiliza límites diferentes por nivel de suscripción y por endpoint.
- Excluye los health checks, las llamadas internas y los assets estáticos.
- Prioriza las ralentizaciones temporales frente a los baneos permanentes.
- Monitorea las tasas de rechazo y ajusta los valores; un pico de errores 429 puede indicar que un límite es demasiado restrictivo.
Mejores prácticas
- Identifica a los clientes utilizando la clave estable más específica disponible.
- Elige un algoritmo que se adapte a la forma del tráfico.
- Devuelve un error 429 con
Retry-Aftery los encabezados de rate limit. - Comparte los contadores en Redis o aplica los límites en el edge.
- Separa los rate limits de las cuotas y expón ambos.
- Permite ráfagas (bursts) y establece los límites basándote en mediciones reales.
- Registra y monitorea los rechazos, y configura alertas para picos repentinos.
Errores comunes
- Contar por instancia y multiplicar el límite efectivo.
- Utilizar únicamente la IP, lo que penaliza a los usuarios detrás de un NAT compartido.
- Devolver un código 200 o descartar las solicitudes silenciosamente.
- Establecer límites tan estrictos que se bloqueen los clientes normales.
- Olvidar expirar los contadores, provocando fugas de memoria.
- Tratar los rate limits como un sustituto de la autenticación y la autorización.
Próximos pasos
El rate limiting mantiene una API disponible bajo presión. Basalo en el diseño REST y la guía de HTTP, combínalo con el versionado de API como parte del ciclo de vida e impleméntalo en Node.js. Después, añade un limitador a un endpoint y observa cómo se comporta bajo una prueba de carga.