Skip to content
Plurafy

API Rate Limiting and Throttling

Rate limiting protects APIs from abuse, ensures fair resource allocation, and maintains system stability. Learn to implement rate limiting strategies and throttling mechanisms.

Implement effective rate limiting and throttling to prevent API abuse while maintaining good user experience and system stability.

Fixed Window: Count requests in fixed time intervals Sliding Window: Smooth rate limiting with overlapping windows Token Bucket: Allow bursts while maintaining average rate Leaky Bucket: Smooth output rate regardless of input

429 Too Many Requests: Rate limit exceeded X-RateLimit-Limit: Total requests allowed X-RateLimit-Remaining: Requests remaining X-RateLimit-Reset: When limit resets

class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate; // tokens per second
this.lastRefillTime = Date.now();
}
refill() {
const now = Date.now();
const timePassed = (now - this.lastRefillTime) / 1000;
const tokensToAdd = timePassed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefillTime = now;
}
tryConsume(tokens = 1) {
this.refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return true;
}
return false;
}
}
module.exports = TokenBucket;
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
// General API limiter
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
standardHeaders: true, // Return rate limit info in `RateLimit-*` headers
legacyHeaders: false, // Disable `X-RateLimit-*` headers
});
// Stricter limiter for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 5 requests per 15 minutes
skipSuccessfulRequests: true, // Don't count successful requests
message: 'Too many login attempts, please try again later.',
});
// Apply limiters
app.use('/api/', limiter);
app.post('/api/auth/login', authLimiter, (req, res) => {
res.json({ message: 'Login successful' });
});
app.listen(3000);
const redis = require('redis');
class RedisRateLimiter {
constructor(client) {
this.client = client;
}
async isAllowed(key, limit, windowSeconds) {
const current = await this.client.incr(key);
if (current === 1) {
await this.client.expire(key, windowSeconds);
}
return {
allowed: current <= limit,
remaining: Math.max(0, limit - current),
resetIn: await this.client.ttl(key),
};
}
async getRateInfo(key) {
const count = await this.client.get(key);
const ttl = await this.client.ttl(key);
return { count: parseInt(count) || 0, ttl };
}
}
module.exports = RedisRateLimiter;
const express = require('express');
const redis = require('redis');
const RedisRateLimiter = require('./redisRateLimiter');
const app = express();
const client = redis.createClient();
const limiter = new RedisRateLimiter(client);
app.use(async (req, res, next) => {
if (!req.user) {
return next();
}
const userId = req.user.id;
const tier = req.user.tier || 'free';
const limits = {
free: { requests: 100, window: 3600 },
pro: { requests: 1000, window: 3600 },
enterprise: { requests: 10000, window: 3600 },
};
const config = limits[tier];
const key = `rate:${userId}:${Math.floor(Date.now() / (config.window * 1000))}`;
const result = await limiter.isAllowed(key, config.requests, config.window);
res.set('X-RateLimit-Limit', config.requests);
res.set('X-RateLimit-Remaining', result.remaining);
res.set('X-RateLimit-Reset', Math.floor(Date.now() / 1000) + result.resetIn);
if (!result.allowed) {
return res.status(429).json({
error: 'Rate limit exceeded',
remaining: result.remaining,
resetIn: result.resetIn,
});
}
next();
});
app.get('/api/data', (req, res) => {
res.json({ data: 'protected data' });
});
app.listen(3000);
class SlidingWindowRateLimiter {
constructor(client) {
this.client = client;
}
async isAllowed(key, limit, windowMs) {
const now = Date.now();
const windowStart = now - windowMs;
// Remove old entries
await this.client.zRemRangeByScore(key, '-inf', windowStart);
// Count current requests
const count = await this.client.zCard(key);
if (count >= limit) {
return { allowed: false, remaining: 0 };
}
// Add current request with timestamp
await this.client.zAdd(key, { score: now, member: `${now}-${Math.random()}` });
await this.client.expire(key, Math.ceil(windowMs / 1000));
return {
allowed: true,
remaining: limit - count - 1,
resetIn: (await this.client.zScore(key, this.client.getLastMember(key))) - now,
};
}
}
module.exports = SlidingWindowRateLimiter;
class AdaptiveRateLimiter {
constructor() {
this.baseLimit = 100;
this.cpuThreshold = 80; // CPU percentage
this.memoryThreshold = 85; // Memory percentage
}
getSystemMetrics() {
const os = require('os');
const cpus = os.cpus();
const totalMem = os.totalmem();
const freeMem = os.freemem();
return {
cpuUsage: Math.random() * 100, // Simplified
memoryUsage: ((totalMem - freeMem) / totalMem) * 100,
};
}
calculateLimit() {
const metrics = this.getSystemMetrics();
let limit = this.baseLimit;
if (metrics.cpuUsage > this.cpuThreshold) {
limit *= 0.5; // Reduce by 50%
}
if (metrics.memoryUsage > this.memoryThreshold) {
limit *= 0.7; // Reduce by 30%
}
return Math.floor(limit);
}
async checkLimit(key) {
const currentLimit = this.calculateLimit();
// Use the dynamic limit in rate limiting logic
return currentLimit;
}
}
module.exports = AdaptiveRateLimiter;
  1. Choose Appropriate Limits

    • Different limits for different endpoints
    • User-tier-based limits
    • Graceful degradation under load
  2. Clear Communication

    • Use standard HTTP headers
    • Provide reset time information
    • Document rate limits in API docs
  3. Fair Distribution

    • Prevent single user/IP from consuming all quota
    • Implement per-endpoint limits
    • Allow burst traffic
  4. Monitor and Alert

    • Track rate limit violations
    • Alert on suspicious patterns
    • Adjust limits based on usage
  5. Handling Overages

    • Return appropriate HTTP status
    • Queue requests instead of rejecting
    • Implement priority queues for critical users
  • Public APIs: Protect infrastructure from abuse
  • SaaS Platforms: Enforce usage tiers
  • Payment APIs: Prevent fraud and abuse
  • Content Delivery: Fair bandwidth allocation
  • Authentication: Prevent brute force attacks