This content originally appeared on DEV Community and was authored by shiva shanker
Your API got hammered by bots? Server bills through the roof? Been there! Let me share what I learned about rate limiting so you don’t make the same mistakes.
TL;DR (Too Long; Didn’t Read)
Rate limiting = API bouncer. Controls requests per time window. Essential for performance, cost control, and preventing abuse. Easy to implement with express-rate-limit.
What’s Rate Limiting?
Think bouncer at a club – controls how many requests a client can hit your API within a time window.
Examples:
- 100 requests/minute per user
- 1000 requests/hour per IP
- 10,000 requests/day for premium users
Why You Need It (Trust Me)
1. Prevent API Abuse
Bots can spam thousands of requests and crash your server. Seen it happen too many times.
2. Fair Usage
One heavy user shouldn’t slow down everyone else.
3. Cost Control
Each API call = money (server resources, DB queries, third-party APIs).
4. Better Performance
Less spam = faster API for real users.
Quick Implementation
Node.js + Express (easiest way):
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests, please try again later.'
});
app.use('/api/', limiter);
Boom! Your API is now protected.
Advanced Patterns
Different limits for different endpoints:
// Strict for auth
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5 // only 5 login attempts per 15 min
});
// Relaxed for public data
const publicLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 1000
});
app.use('/api/auth/', authLimiter);
app.use('/api/public/', publicLimiter);
Redis for distributed systems:
const RedisStore = require('rate-limit-redis');
const redis = require('redis');
const client = redis.createClient();
const limiter = rateLimit({
store: new RedisStore({
client: client,
}),
windowMs: 15 * 60 * 1000,
max: 100
});
Pro Tips From Experience
DO:
- Start with conservative limits, adjust based on real usage
- Use different limits for different endpoints
- Provide clear error messages with retry info
- Monitor and log rate limit hits
DON’T:
- Set limits too low (frustrated users)
- Set limits too high (no protection)
- Forget to whitelist monitoring tools
- Ignore mobile app retry behavior
Better Error Responses
Instead of generic “Too many requests”:
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: {
error: 'Rate limit exceeded',
retryAfter: '15 minutes',
limit: 100,
windowMs: 15 * 60 * 1000
}
});
Testing Rate Limits
Quick script to test your limits:
// test-rate-limit.js
const axios = require('axios');
async function testRateLimit() {
for (let i = 0; i < 105; i++) {
try {
const response = await axios.get('http://localhost:3000/api/test');
console.log(`Request ${i + 1}: ${response.status}`);
} catch (error) {
console.log(`Request ${i + 1}: ${error.response.status} - ${error.response.data.message}`);
}
}
}
testRateLimit();
Real-World Example
Here’s how I implemented it for a small startup:
const rateLimit = require('express-rate-limit');
const MongoStore = require('rate-limit-mongo');
// Different tiers
const freeTierLimit = rateLimit({
store: new MongoStore({
uri: process.env.MONGODB_URI,
collectionName: 'rateLimits',
}),
windowMs: 24 * 60 * 60 * 1000, // 24 hours
max: 1000, // 1000 requests per day for free users
keyGenerator: (req) => req.user.id
});
const premiumTierLimit = rateLimit({
store: new MongoStore({
uri: process.env.MONGODB_URI,
collectionName: 'rateLimits',
}),
windowMs: 24 * 60 * 60 * 1000,
max: 10000, // 10k requests per day for premium
keyGenerator: (req) => req.user.id
});
// Apply based on user tier
app.use('/api/', (req, res, next) => {
if (req.user.tier === 'premium') {
premiumTierLimit(req, res, next);
} else {
freeTierLimit(req, res, next);
}
});
Monitoring Rate Limits
Track important metrics:
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
onLimitReached: (req) => {
console.log(`Rate limit hit for IP: ${req.ip}`);
// Send to monitoring service
analytics.track('rate_limit_exceeded', {
ip: req.ip,
endpoint: req.path,
userAgent: req.get('User-Agent')
});
}
});
Common Gotchas
- Mobile apps retry automatically – factor this into your limits
- Load balancers can mess with IP detection – use proper headers
- Don’t rate limit health checks – whitelist monitoring endpoints
- Consider user experience – show remaining requests in headers
Useful links:
Conclusion
Rate limiting isn’t optional anymore. Even small APIs need protection. Start simple, monitor usage, adjust as needed.
Your server bills will thank you!
What’s your rate limiting setup? Share your configs in the comments!
This content originally appeared on DEV Community and was authored by shiva shanker