Introduction
An API may be technically correct and still become unavailable if a small number of clients send too many requests. Rate limiting helps protect application resources, maintain fair access, and reduce the impact of abusive traffic.
A useful policy should reflect the cost of the endpoint and the identity of the caller. One global request limit rarely fits every route.
1. Configure a rate-limiting policy
ASP.NET Core provides rate-limiting middleware. A fixed-window policy is straightforward to configure:
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddFixedWindowLimiter("api", limiter =>
{
limiter.PermitLimit = 100;
limiter.Window = TimeSpan.FromMinutes(1);
limiter.QueueLimit = 0;
limiter.AutoReplenishment = true;
});
});
var app = builder.Build();
app.UseRouting();
app.UseRateLimiter();
app.MapGet("/api/products", () =>
Results.Ok(new[] { "Keyboard", "Monitor" }))
.RequireRateLimiting("api");
app.Run();
This example allows up to 100 requests per one-minute window for the selected policy. The appropriate values depend on your workload, and the policy should be tested under realistic traffic.
2. Choose the correct partition key
A global limiter treats every caller as one group. That can let a single client consume capacity intended for everyone.
Depending on the application, partition requests by authenticated user, API key, tenant, or client IP address.
IP-based limits need special care behind reverse proxies and load balancers. Only trust forwarded headers from known proxy infrastructure; otherwise, clients may spoof address information or unrelated users may share a limit.
For authenticated APIs, per-user or per-tenant policies are often more meaningful than a single global limit.
3. Match the policy to the endpoint
A product-list endpoint may be cheap, while a report-generation endpoint may consume significant CPU or database resources.
Use stricter policies for expensive operations such as exports, search queries, login attempts, and resource-intensive transformations. Authentication endpoints may also need protections against credential stuffing, alongside rate limiting.
Do not rely on rate limiting as the only security control. It complements authorization, input validation, monitoring, and abuse detection.
4. Return useful responses
When a client exceeds its limit, return 429 Too Many Requests. Where appropriate, provide retry guidance through headers or a response body.
Clients should handle this response gracefully rather than retrying immediately in a tight loop. Document the policy so integrators can design their own backoff behavior.
5. Account for multiple application instances
An in-memory limiter may enforce limits separately on each application instance. If your service runs across several replicas, the effective total limit may differ from the intended per-client limit.
For strict distributed limits, you may need a shared coordination mechanism or gateway-level policy. Consider the extra latency, availability, and operational complexity before choosing a distributed design.
Conclusion
Rate limiting is most effective when it reflects endpoint cost, uses a trustworthy identity key, and returns clear guidance to clients. Test the policy under load and verify how it behaves across multiple instances before relying on it as a production safeguard.