API Rate Limiting
Implement rate limiting to protect APIs from abuse and overload.
A Simple Analogy
Rate limiting is like a bouncer at a nightclub. They control how many people enter per minute to prevent overcrowding. Without limits, the club becomes unusable for everyone. APIs need bouncers too.
What Is Rate Limiting?
Rate limiting controls how many requests a client can make to an API in a time period. It prevents abuse, ensures fair usage, and protects server resources.
Why Implement Rate Limiting?
- Prevent abuse: Stop malicious users from overwhelming servers
- Fair usage: Ensure all users get access
- Stability: Prevent cascading failures
- Predictability: Smooth traffic patterns
- Revenue: Different tiers get different limits
Rate Limiting Strategies
| Strategy | How It Works | Use Case | |----------|-------------|----------| | Token Bucket | Tokens replenish over time | Bursts allowed | | Sliding Window | Count requests in time window | Strict limits | | Leaky Bucket | Drain at constant rate | Smooth traffic | | Fixed Window | Reset counter at intervals | Simple, predictable |
.NET Implementation
// Using AspNetCoreRateLimit package
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddInMemoryRateLimiting();
var ipRateLimitPolicies = new List<IpRateLimitPolicy>
{
new IpRateLimitPolicy
{
IpPrefix = "*", // All IPs
Rules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint = "*", // All endpoints
Limit = 100, // 100 requests
Period = "1m" // per 1 minute
}
}
}
};
builder.Services.Configure<IpRateLimitOptions>(options =>
{
options.IpWhitelist = new List<string> { "127.0.0.1" };
options.Policies = ipRateLimitPolicies;
});
var app = builder.Build();
app.UseIpRateLimiting();
Custom Rate Limiting Middleware
public class RateLimitMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private const int MaxRequests = 100;
private const int WindowSeconds = 60;
public RateLimitMiddleware(RequestDelegate next, IMemoryCache cache)
{
_next = next;
_cache = cache;
}
public async Task InvokeAsync(HttpContext context)
{
var clientId = context.Connection.RemoteIpAddress?.ToString();
var cacheKey = $"ratelimit_{clientId}";
if (!_cache.TryGetValue(cacheKey, out int requestCount))
{
requestCount = 0;
}
if (requestCount >= MaxRequests)
{
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.Response.WriteAsync("Rate limit exceeded");
return;
}
_cache.Set(cacheKey, requestCount + 1, TimeSpan.FromSeconds(WindowSeconds));
await _next(context);
}
}
// Register middleware
app.UseMiddleware<RateLimitMiddleware>();
API Response Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1640000000
HTTP 429 Too Many Requests
Retry-After: 30
Practical Example
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
[HttpGet]
public IActionResult GetData()
{
var clientIp = HttpContext.Connection.RemoteIpAddress?.ToString();
// Check rate limit
// If exceeded, return 429
return Ok(new { message = "Data" });
}
}
Best Practices
- Include headers: Tell clients their limit status
- Graceful degradation: Return useful error messages
- Whitelist: Exclude internal services
- Tiered limits: Different limits for different API tiers
- Monitor: Track rate limit violations
Related Concepts to Explore
- Distributed rate limiting (Redis)
- Per-user vs. per-IP limiting
- Adaptive rate limiting
- API quota management
- DDoS protection
Summary
Rate limiting protects APIs by controlling request volume. Implement appropriate limits to prevent abuse, ensure fair usage, and maintain service stability.