Public APIs are constantly exposed to unpredictable traffic. A sudden spike in legitimate requests, an aggressive web crawler, or a malicious bot can overwhelm application resources and degrade performance for every user. Without proper request throttling, APIs may experience increased latency, excessive infrastructure costs, or even complete service outages.
ASP.NET Core includes a built-in Rate Limiting middleware that allows developers to control how many requests clients can make over a specific period. By applying appropriate rate limiting policies, applications can protect backend services, improve fairness among users, and maintain consistent performance under heavy load.
Rather than relying solely on external API gateways, this article explains how to implement production-ready rate limiting directly in ASP.NET Core.
Note: Rate limiting is not a replacement for authentication, authorization, or DDoS protection. It is one layer of a broader API security strategy.
Why Rate Limiting Matters
Without rate limiting, applications may encounter:
Limiting request rates helps maintain predictable application performance.
Common Rate Limiting Algorithms
ASP.NET Core supports several algorithms.
| Algorithm | Best For |
|---|
| Fixed Window | Simple APIs |
| Sliding Window | Public APIs |
| Token Bucket | Burst traffic |
| Concurrency Limiter | Long-running requests |
Each algorithm balances fairness, performance, and implementation complexity differently.
Rate Limiting Architecture
flowchart LR
A[Client]
B[Rate Limiter]
C{Limit Exceeded?}
D[ASP.NET Core API]
E[(Database)]
A --> B
B --> C
C -->|No| D
D --> E
C -->|Yes| F[HTTP 429 Too Many Requests]
Every incoming request passes through the rate limiter before reaching the application.
Installing the Middleware
Register rate limiting during application startup.
builder.Services.AddRateLimiter(options =>
{
});
Enable the middleware.
var app = builder.Build();
app.UseRateLimiter();
app.MapControllers();
app.Run();
The middleware processes requests before controller actions execute.
Configuring a Fixed Window Policy
A fixed window policy allows a specific number of requests during a defined time period.
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter(
"fixed",
limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window =
TimeSpan.FromMinutes(1);
limiterOptions.QueueLimit = 0;
});
});
This policy permits up to 100 requests per minute.
Applying a Rate Limit
Apply the policy to an endpoint.
app.MapGet("/products", GetProducts)
.RequireRateLimiting("fixed");
Only requests to this endpoint are subject to the configured policy.
Using a Sliding Window
Sliding windows distribute requests more evenly.
builder.Services.AddRateLimiter(options =>
{
options.AddSlidingWindowLimiter(
"sliding",
limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window =
TimeSpan.FromMinutes(1);
limiterOptions.SegmentsPerWindow = 6;
});
});
This approach reduces sudden bursts at window boundaries.
Using a Token Bucket
Token Bucket is ideal for bursty traffic.
builder.Services.AddRateLimiter(options =>
{
options.AddTokenBucketLimiter(
"token",
limiterOptions =>
{
limiterOptions.TokenLimit = 200;
limiterOptions.TokensPerPeriod = 20;
limiterOptions.ReplenishmentPeriod =
TimeSpan.FromSeconds(10);
});
});
Clients can consume available tokens quickly, while new tokens are replenished over time.
Request Processing Flow
sequenceDiagram
participant Client
participant RateLimiter
participant API
Client->>RateLimiter: HTTP Request
alt Limit Available
RateLimiter->>API: Forward Request
API-->>Client: HTTP 200
else Limit Exceeded
RateLimiter-->>Client: HTTP 429
end
Requests exceeding the configured policy receive an HTTP 429 response.
Choosing the Right Algorithm
| Scenario | Recommended Algorithm |
|---|
| Internal APIs | Fixed Window |
| Public REST APIs | Sliding Window |
| AI APIs | Token Bucket |
| File Upload APIs | Concurrency Limiter |
| Authentication Endpoints | Sliding Window |
Choose the algorithm based on expected traffic patterns rather than using one policy everywhere.
Customizing Rejected Requests
Provide a meaningful response when requests exceed the limit.
builder.Services.AddRateLimiter(options =>
{
options.OnRejected = async (
context,
cancellationToken) =>
{
context.HttpContext.Response.StatusCode = 429;
await context.HttpContext.Response.WriteAsync(
"Too many requests.",
cancellationToken);
};
});
Clear error messages help API consumers understand why requests were rejected.
Common Production Mistakes
| Problem | Root Cause |
|---|
| Legitimate users blocked | Limits configured too aggressively |
| APIs still overloaded | Limits too permissive |
| Authentication endpoint abuse | No rate limiting applied |
| Inconsistent throttling | Different policies without planning |
| Poor user experience | No retry guidance after HTTP 429 |
| Memory pressure | Excessive request queueing |
Effective rate limiting requires balancing protection with usability.
Best Practices
Apply different policies for different endpoints.
Use stricter limits for authentication endpoints.
Return HTTP 429 for rejected requests.
Include retry guidance where appropriate.
Monitor rejected request metrics.
Combine rate limiting with authentication.
Test policies under realistic production traffic.
Common Anti-Patterns
Avoid these common mistakes:
Applying identical limits to every endpoint.
Ignoring authenticated versus anonymous users.
Using unlimited request queues.
Treating rate limiting as DDoS protection.
Configuring limits without monitoring results.
Forgetting to document API rate limits.
FAQ
Which rate limiting algorithm should I use?
For most public APIs, the Sliding Window algorithm provides a good balance between fairness and predictable request distribution. Token Bucket is better when occasional bursts are expected.
What happens when a client exceeds the limit?
ASP.NET Core returns an HTTP 429 (Too Many Requests) response, indicating that the client has exceeded the configured request limit.
Can different endpoints use different policies?
Yes. Each endpoint can have its own rate limiting policy based on its expected workload and security requirements.
Does rate limiting work with Minimal APIs?
Yes. The built-in Rate Limiting middleware supports Minimal APIs, MVC controllers, Razor Pages, and endpoint routing.
Conclusion
Rate limiting is an essential safeguard for modern ASP.NET Core APIs. By controlling request volume, applications can prevent abuse, protect backend resources, and maintain consistent performance during periods of heavy traffic.
ASP.NET Core's built-in Rate Limiting middleware provides flexible algorithms such as Fixed Window, Sliding Window, Token Bucket, and Concurrency Limiter, making it easy to tailor request throttling for different workloads. Combined with authentication, monitoring, and API gateways, rate limiting helps build secure, scalable, and resilient applications ready for production environments.