Public APIs are constantly exposed to unpredictable traffic. Whether it's accidental overuse, poorly designed client applications, or malicious attacks, excessive requests can degrade performance, increase infrastructure costs, and even make an application unavailable.
Rate limiting helps protect APIs by controlling how many requests a client can make within a specified period. Starting with .NET 7, ASP.NET Core includes built-in rate limiting middleware, making it easier to implement robust traffic control without relying on third-party libraries.
In this article, you'll learn how rate limiting works, explore the available algorithms, and discover best practices for building secure and scalable ASP.NET Core APIs.
Why Rate Limiting Matters
Consider a public product API that receives thousands of requests from different clients.
Without rate limiting:
Clients
│
ASP.NET Core API
│
Database
A single client could send excessive requests, consuming server resources and affecting other users.
With rate limiting:
Clients
│
Rate Limiter
│
ASP.NET Core API
│
Database
The application accepts requests within defined limits while rejecting excessive traffic with an HTTP 429 Too Many Requests response.
Rate limiting helps:
Protect server resources
Prevent abuse
Improve application stability
Ensure fair usage among clients
Reduce the impact of denial-of-service attempts
Built-In Rate Limiting Middleware
ASP.NET Core includes rate limiting middleware in the Microsoft.AspNetCore.RateLimiting package.
Register rate limiting during application startup:
using System.Threading.RateLimiting;
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", limiter =>
{
limiter.PermitLimit = 100;
limiter.Window = TimeSpan.FromMinutes(1);
});
});
Enable the middleware:
app.UseRateLimiter();
Apply the policy to an endpoint:
app.MapGet("/products", () => Results.Ok())
.RequireRateLimiting("fixed");
This configuration limits clients to 100 requests per minute for the protected endpoint.
Fixed Window Rate Limiting
The fixed window algorithm divides time into fixed intervals.
Example:
Window: 1 minute
Limit: 100 requests
A client can make up to 100 requests during the current minute. Once the limit is reached, additional requests are rejected until the next window begins.
Best Use Cases
Public REST APIs
Simple application throttling
Low-complexity workloads
Its simplicity makes it easy to configure, although it can allow request bursts at window boundaries.
Sliding Window Rate Limiting
Sliding window limiting distributes requests more evenly over time.
Instead of resetting all counters simultaneously, it continuously evaluates activity over the most recent time period.
Benefits include:
Smoother request distribution
Reduced traffic spikes
More consistent resource utilization
This approach is often preferable for APIs with fluctuating traffic.
Token Bucket Algorithm
The token bucket algorithm provides greater flexibility.
Tokens are added to a bucket at a fixed rate.
Each incoming request consumes one token.
If tokens remain:
Request succeeds.
If no tokens remain:
Request is rejected.
This algorithm allows short traffic bursts while maintaining an average request rate.
It's commonly used for public APIs that experience occasional spikes in demand.
Concurrency Limiting
Some operations are expensive regardless of request frequency.
Concurrency limiting controls how many requests execute simultaneously.
Example configuration:
builder.Services.AddRateLimiter(options =>
{
options.AddConcurrencyLimiter("concurrent", limiter =>
{
limiter.PermitLimit = 20;
});
});
Only 20 requests are processed concurrently, while additional requests wait or are rejected depending on the configuration.
This strategy works well for resource-intensive endpoints.
Choosing a Partition Key
Rate limiting policies are typically applied per client rather than globally.
Common partition keys include:
Client IP address
Authenticated user ID
API key
Tenant identifier
Selecting the appropriate partition ensures one client cannot exhaust resources allocated to others.
For multi-tenant applications, API keys or tenant identifiers usually provide better isolation than IP addresses.
Handling Rejected Requests
When a request exceeds the configured limit, the API should return a clear response.
Typical response:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Including the Retry-After header helps clients determine when they can safely retry.
Well-designed APIs document their rate limits and expected behavior so client applications can respond appropriately.
Rate Limiting Strategies Compared
| Strategy | Best For | Advantages | Limitations |
|---|---|---|---|
| Fixed Window | Simple APIs | Easy to configure | Allows burst traffic at window boundaries |
| Sliding Window | Public APIs | Smoother request distribution | Slightly more complex |
| Token Bucket | Variable workloads | Supports controlled bursts | Requires token management |
| Concurrency Limiter | Resource-intensive endpoints | Protects expensive operations | Doesn't directly limit request frequency |
Best Practices
Apply different limits to different endpoints based on their resource requirements.
Use authenticated identities or API keys instead of IP addresses whenever possible.
Return HTTP 429 responses with helpful retry information.
Monitor rejected requests to identify abuse or misconfigured clients.
Combine rate limiting with authentication and authorization.
Test rate limiting policies under realistic workloads before deploying to production.
Document API limits for consumers.
Common Mistakes
Applying the Same Limit Everywhere
Not every endpoint has the same cost. A lightweight health check and a report generation endpoint shouldn't share identical limits. Tailor policies to the workload.
Using Only IP-Based Limits
Many users may share the same public IP address, especially in corporate environments or behind proxies. Authenticated user IDs or API keys often provide fairer rate limiting.
Ignoring Monitoring
Without monitoring, it's difficult to determine whether clients are hitting limits because of abuse or legitimate usage. Track rejected requests and adjust policies based on actual traffic patterns.
Treating Rate Limiting as a Security Solution
Rate limiting helps reduce abuse but doesn't replace authentication, authorization, input validation, or web application firewalls. It should be part of a broader API security strategy.
Conclusion
Rate limiting is an essential component of modern API design. By controlling request volume, it protects applications from abuse, improves resource utilization, and ensures fair access for all clients.
ASP.NET Core's built-in rate limiting middleware makes it straightforward to implement policies such as fixed window, sliding window, token bucket, and concurrency limiting without relying on external libraries. Choosing the right strategy depends on your application's traffic patterns, endpoint characteristics, and scalability requirements.
When combined with authentication, monitoring, and well-designed API policies, rate limiting helps build secure, reliable, and scalable ASP.NET Core applications that continue to perform well under both expected and unexpected traffic loads.

Join the conversation! Your thoughts help the community grow.