Implementing API Rate Limiting
API rate limiting in distributed systems requires consistent state management, careful algorithm selection, and clear communication with clients.
Overview
API rate limiting is the practice of restricting the number of requests a client can make to an API. In distributed systems, limits must be enforced consistently across all instances, which typically requires a centralized store like Redis or a database. The main algorithms are Fixed Window, Sliding Window, Token Bucket, and Leaky Bucket, each with different trade-offs in fairness, burst tolerance, and implementation complexity. Key implementation details include client identification, atomic operations, status headers, proper error handling with 429 responses and Retry-After, and choosing appropriate limits. Rate limiting should be enforced at multiple layers: in the API Gateway or Load Balancer for broad protection, and in application code for fine-grained, user-specific rules.
Key Components
Client Identification
Rate limiting needs a unique way to identify each client. You can use API keys, user IDs, IP addresses, or combinations thereof. For authenticated clients, user ID is typically the most reliable choice, since IP addresses change and multiple clients can share a single IP.
Fixed Window Implementation
Fixed Window counts requests within fixed time periods. In Redis, you store a counter for each window and client. The counter is created on the first request within a window and expires after the time elapses. If the counter exceeds the limit, the request is rejected.
Sliding Window Implementation
Sliding Window is fairer than Fixed Window but more complex. One approach is Sliding Window Log, which stores a list of timestamps for each client’s recent requests. Requests outside the window are removed. If the count of remaining requests exceeds the limit, the new request is rejected.
Token Bucket Implementation
Token Bucket is a widely used algorithm. A bucket has a maximum capacity and refills with tokens at a constant rate. Each request consumes one token. If the bucket is empty, the request is rejected or queued. Redis with Lua scripts works well for atomic token consumption.
Leaky Bucket Implementation
Leaky Bucket processes requests at a constant rate. Requests are queued and drained uniformly. This algorithm produces smooth traffic but can introduce latency and drop requests if the queue fills.
Redis for Distributed Rate Limiting
Redis provides fast atomic operations and TTL support, making it ideal for rate limiting. Lua scripts enable multiple commands to execute atomically. Redis Cluster or Redis Sentinel provide high availability. At very high request volumes, Redis can become a central bottleneck, so local caching or edge proxies may be added.
Atomic Operations
Rate limiting must be atomic to avoid race conditions. When multiple concurrent requests check whether limit is available, the limit must never be exceeded. Lua scripts in Redis or database transactions ensure atomicity.
Status Headers
Clients should know their current quota status. Headers like X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are widely used. Alternatively, the RFC RateLimit schema can be applied. These headers let clients adjust their request rate proactively.
Error Handling with 429
When the limit is exceeded, the API responds with 429 Too Many Requests. The Retry-After header should indicate when the client may retry. The error response should follow RFC 7807 Problem Details format and avoid exposing internal details.
Per-Endpoint and Per-User Limits
Different endpoints have different costs. Write operations and expensive queries should have stricter limits than simple reads. Similarly, different user tiers—free versus paying customers, for example—can have different allowances.
Rate Limiting in API Gateways
API Gateways like Kong, Nginx, or AWS API Gateway have built-in rate limiting. They protect the API before requests reach the backend. Gateways work well for global limits and simple rules, while application code can enforce complex, user-specific policies.
Practical Example
A Node.js service implements Token Bucket rate limiting with Redis.
const redis = require('redis');
const client = redis.createClient();
const LIMIT = 100;
const WINDOW_SECONDS = 60;
async function isAllowed(clientId) {
const key = `rate_limit:${clientId}`;
const lua = `
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('GET', key)
if current == false then
current = 0
end
current = tonumber(current)
if current >= limit then
return 0
end
redis.call('INCR', key)
if current == 0 then
redis.call('EXPIRE', key, window)
end
return 1
`;
const result = await client.eval(lua, { keys: [key], arguments: [String(LIMIT), String(WINDOW_SECONDS)] });
return result === 1;
}
Express middleware:
async function rateLimit(req, res, next) {
const clientId = req.user?.id || req.ip;
const allowed = await isAllowed(clientId);
if (!allowed) {
res.set('Retry-After', String(WINDOW_SECONDS));
return res.status(429).json({
type: 'https://api.example.com/problems/rate-limit-exceeded',
title: 'Rate Limit Exceeded',
status: 429,
detail: `You have exceeded the limit of ${LIMIT} requests per ${WINDOW_SECONDS} seconds.`
});
}
next();
}
This approach is atomic, works across multiple server instances, and communicates limits clearly to the client.
FAQ: API Rate Limiting Implementation
1. How do you identify a client for rate limiting?
2. Why is Redis suitable for rate limiting?
3. What is a Token Bucket in practice?
4. What is atomic rate limiting?
5. What is Sliding Window Log?
6. What is Fixed Window?
7. What is Leaky Bucket?
8. What is a race condition in rate limiting?
9. Which headers should be set for rate limiting?
10. What is 429 Too Many Requests?
11. Should rate limiting happen in the gateway or in application code?
12. What are per-endpoint limits?
13. What is a rate limiting bottleneck?
14. What makes a good rate limiting strategy?
15. What is the benefit of Retry-After?
Next in the API Learning Path
The next article in the API learning path covers Rate Limiting and Throttling for APIs — strategies and algorithms for rate limiting, plus the key differences between rate limiting and throttling.
References
- https://www.rfc-editor.org/rfc/rfc6585
- https://redis.io/docs/manual/programmability/eval-intro/
- https://www.konghq.com/kong
Recommended Reading on API Development
If you’d like to dive deeper into rate limiting, distributed systems, and API architecture, check out these books:
API Development
Books about API design, REST, GraphQL, OpenAPI and API architecture
Designing Data-Intensive Applications von Martin Kleppmann
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
API Design Patterns von JJ Geewax
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.




