Introduction
As APIs grow in popularity, they often face challenges related to excessive traffic, abusive requests, bot activity, and accidental overuse by clients. Without proper safeguards, a sudden spike in requests can overwhelm servers, degrade performance, increase infrastructure costs, and negatively impact the user experience.
Rate limiting is one of the most effective techniques for protecting APIs from these issues. It controls how many requests a client can make within a specific period, ensuring that resources remain available for all users. Modern versions of ASP.NET Core include built-in rate limiting middleware, making it easier than ever to implement API protection without relying on third-party libraries.
In this article, you'll learn why rate limiting is important, explore common rate limiting strategies, and discover best practices for implementing rate limiting in high-traffic ASP.NET Core APIs.
What Is Rate Limiting?
Rate limiting is a mechanism that restricts the number of requests a client can send to an API during a defined time window.
For example:
100 requests per minute
1,000 requests per hour
10 requests per second
When the limit is exceeded, the API typically responds with:
HTTP 429 Too Many Requests
This prevents individual clients from consuming excessive resources and helps maintain overall system stability.
Rate limiting is commonly used to:
Prevent abuse
Protect backend services
Improve application reliability
Reduce infrastructure costs
Ensure fair resource usage
Why High-Traffic APIs Need Rate Limiting
Imagine an e-commerce API receiving thousands of requests every second.
Without rate limiting:
Clients
↓
API
↓
Database
A single malfunctioning client or automated bot could generate enough traffic to overload the entire system.
With rate limiting:
Clients
↓
Rate Limiter
↓
API
↓
Database
Excessive requests are blocked before they reach critical resources.
This reduces pressure on:
Application servers
Databases
External services
Caching layers
As a result, legitimate users continue to receive reliable service.
Common Rate Limiting Strategies
ASP.NET Core supports several rate limiting approaches.
Fixed Window Limiting
This is the simplest strategy.
Example:
100 requests per minute
A counter tracks requests within a fixed time window.
Advantages:
Easy to implement
Predictable behavior
Limitations:
Traffic spikes may occur when windows reset
Sliding Window Limiting
Sliding windows distribute requests more evenly.
Instead of resetting at fixed intervals, the limit continuously evaluates recent activity.
Benefits include:
Smoother traffic control
Reduced burst behavior
This approach is often preferred for public APIs.
Token Bucket Limiting
A bucket contains a fixed number of tokens.
Each request consumes a token.
When tokens run out, requests are rejected until tokens are replenished.
Benefits:
Supports occasional bursts
Provides flexible traffic management
This strategy works well for APIs with variable traffic patterns.
Concurrency Limiting
Concurrency limits focus on active requests rather than request counts.
Example:
Maximum 50 concurrent requests
This protects APIs from resource exhaustion caused by long-running operations.
Implementing Rate Limiting in ASP.NET Core
ASP.NET Core provides built-in middleware for rate limiting.
Register rate limiting services:
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter(
"ApiPolicy",
limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window =
TimeSpan.FromMinutes(1);
});
});
Enable middleware:
app.UseRateLimiter();
Apply the policy:
app.MapGet("/products", () =>
{
return Results.Ok();
})
.RequireRateLimiting("ApiPolicy");
The endpoint now allows a maximum of 100 requests per minute.
Rate Limiting by Client Identity
Global limits are useful, but most production APIs require per-client limits.
Clients can be identified using:
API keys
User IDs
IP addresses
Access tokens
Example:
options.AddPolicy("PerUser", context =>
{
return RateLimitPartition.GetFixedWindowLimiter(
context.User.Identity?.Name ??
"anonymous",
_ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
});
});
This ensures each user receives an independent quota.
Without partitioning, a single heavy user could affect everyone else.
Protect Expensive Endpoints Separately
Not all API endpoints have the same resource requirements.
For example:
GET /products
may be inexpensive.
However:
POST /generate-report

Join the conversation! Your thoughts help the community grow.