How to Add Rate Limiting to an API
Protect an API with token-bucket rate limiting keyed per client, backed by Redis so limits hold across instances. Return standard RateLimit headers and 429 with Retry-After when exceeded.
What and why
Rate limiting caps how many requests a client may make in a window, protecting an API from abuse, accidental floods, and runaway costs. It also enforces fair use across tenants. Because most APIs run multiple instances, limits should be shared, typically in Redis.
Prerequisites
- A running API.
- Redis available for shared counters.
- Basic HTTP knowledge.
Steps
1. Choose a limiting algorithm
Token bucket allows short bursts while capping the long-term rate, and is the most common choice. Sliding window gives smoother enforcement. Fixed window is simplest but allows double bursts at window edges.
2. Identify the client key
Limit per API key for authenticated clients, falling back to client IP for anonymous traffic. Be careful behind proxies: trust the correct forwarded header, or attackers spoof it.
3. Enforce limits with Redis
Use an atomic operation so concurrent requests count correctly. With express-rate-limit and a Redis store:
const rateLimit = require('express-rate-limit');
const { RedisStore } = require('rate-limit-redis');
app.use(rateLimit({
windowMs: 60_000,
limit: 100,
standardHeaders: 'draft-7',
store: new RedisStore({ sendCommand: (...a) => redis.call(...a) }),
keyGenerator: (req) => req.apiKey || req.ip,
}));
A shared store means the limit holds across all instances.
4. Return rate-limit headers
Return the standard headers so clients can self-throttle:
RateLimit-Limit: 100
RateLimit-Remaining: 12
RateLimit-Reset: 30
When the limit is exceeded, respond 429 Too Many Requests with a Retry-After header.
5. Handle bursts and exemptions
Allow a small burst above the steady rate for legitimate spikes, and exempt health checks and internal traffic. Apply tiered limits by plan (free vs paid) using different keys.
Verification
- Exceeding the limit returns 429 with
Retry-After. - Remaining-count headers decrement across requests.
- Limits hold when requests hit different instances.
Next Steps
Move limiting to the edge (an API gateway or Nginx limit_req) to shed load before it reaches your app, add per-endpoint limits for expensive routes, and emit metrics so you can tune thresholds from real traffic.
Prerequisites
- A running API
- Redis available
- Basic HTTP knowledge
Steps
- 1Choose a limiting algorithm
- 2Identify the client key
- 3Enforce limits with Redis
- 4Return rate-limit headers
- 5Handle bursts and exemptions