← Back to Article List         
How to implement Rate Limiting in ASP.NET Core Web API

How to implement Rate Limiting in ASP.NET Core Web API

Published on 19 Sep 2026     5 min read Web API
Rate Limiting

Rate Limiting in ASP.NET Core Web API

What is rate limiting?

Rate limiting controls how many requests a client can send to an API during a specified period.

Example:

Allow a maximum of 5 requests every 10 seconds.

If the client sends more than five requests within ten seconds, the API rejects the additional requests with:

HTTP 429 Too Many Requests

Why do we need rate limiting?

Rate limiting helps to:

  • Protect the API from excessive traffic

  • Prevent API abuse

  • Reduce server and database load

  • Ensure fair usage among clients

  • Control costly operations

  • Improve application stability

  • Partially reduce denial-of-service attacks

Rate limiting helps with abusive traffic, but it is not complete DDoS protection. Production systems should also use services such as a WAF, CDN, Azure Front Door or Cloudflare.


Simple fixed-window example

This example allows:

5 requests every 10 seconds

Step 1: Configure rate limiting in Program.cs

using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddRateLimiter(options =>
{
    // Response status when the limit is exceeded
    options.RejectionStatusCode =
        StatusCodes.Status429TooManyRequests;

    // Create a named rate-limiting policy
    options.AddFixedWindowLimiter(
        policyName: "FixedPolicy",
        configureOptions: limiterOptions =>
        {
            // Maximum requests allowed in one window
            limiterOptions.PermitLimit = 5;

            // Duration of one window
            limiterOptions.Window =
                TimeSpan.FromSeconds(10);

            // Do not wait when the limit is exceeded
            limiterOptions.QueueLimit = 0;

            limiterOptions.QueueProcessingOrder =
                QueueProcessingOrder.OldestFirst;

            // Automatically start a new window
            limiterOptions.AutoReplenishment = true;
        });
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRateLimiter();

app.MapControllers();

app.Run();

Step 2: Apply the policy to a controller

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace RateLimitDemo.Controllers;

[ApiController]
[Route("api/[controller]")]
[EnableRateLimiting("FixedPolicy")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProducts()
    {
        var products = new[]
        {
            "Laptop",
            "Mobile",
            "Keyboard"
        };

        return Ok(products);
    }
}

The rate-limiting policy now applies to every endpoint inside ProductsController.


How the example works

Call this endpoint repeatedly:

GET /api/products

Within the same 10-second window:

Request Result
1 200 OK
2 200 OK
3 200 OK
4 200 OK
5 200 OK
6 429 Too Many Requests
7 429 Too Many Requests

After the 10-second window ends, the counter resets, and another five requests are allowed.


Code explanation

AddRateLimiter()

builder.Services.AddRateLimiter(options =>
{
    // Policies
});

Registers ASP.NET Core’s built-in rate-limiting services.

AddFixedWindowLimiter()

options.AddFixedWindowLimiter(
    policyName: "FixedPolicy",
    configureOptions: limiterOptions =>
    {
        // Settings
    });

Creates a named policy using the fixed-window algorithm.

The name "FixedPolicy" is later used to apply the policy to controllers or endpoints.

PermitLimit

limiterOptions.PermitLimit = 5;

Specifies the maximum number of requests allowed in one window.

Here, only five requests are permitted.

Window

limiterOptions.Window = TimeSpan.FromSeconds(10);

Defines the duration of one window.

Therefore:

5 requests / 10 seconds

QueueLimit

limiterOptions.QueueLimit = 0;

Specifies how many extra requests may wait in a queue.

Because it is 0, extra requests are rejected immediately with 429.

If it were:

limiterOptions.QueueLimit = 2;

two additional requests could wait for permits. Queueing can increase response time, so 0 is often simpler for APIs.

QueueProcessingOrder

limiterOptions.QueueProcessingOrder =
    QueueProcessingOrder.OldestFirst;

When queueing is enabled, the oldest waiting request is processed first.

This setting has no practical effect when QueueLimit is 0.

AutoReplenishment

limiterOptions.AutoReplenishment = true;

ASP.NET Core automatically renews the available request permits when a new window begins.

UseRateLimiter()

app.UseRateLimiter();

Adds the rate-limiting middleware to the HTTP request pipeline.

Registering AddRateLimiter() alone is insufficient. The middleware must also be enabled.

EnableRateLimiting

[EnableRateLimiting("FixedPolicy")]

Applies the named policy to a controller or action.


Apply rate limiting to only one action

Instead of placing the attribute on the controller, it can be placed on an individual action:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    [EnableRateLimiting("FixedPolicy")]
    public IActionResult GetProducts()
    {
        return Ok(new[] { "Laptop", "Mobile" });
    }

    [HttpGet("public")]
    public IActionResult GetPublicInformation()
    {
        return Ok("This endpoint is not rate limited.");
    }
}

Only GET /api/products is rate limited.


Apply rate limiting to all controllers

Remove [EnableRateLimiting] from the controller and change endpoint mapping:

app.MapControllers()
   .RequireRateLimiting("FixedPolicy");

Now the policy applies to all controller endpoints.


Customize the 429 response

A meaningful response helps API clients understand why their request failed.

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode =
        StatusCodes.Status429TooManyRequests;

    options.OnRejected = async (context, cancellationToken) =>
    {
        context.HttpContext.Response.ContentType =
            "application/json";

        await context.HttpContext.Response.WriteAsJsonAsync(
            new
            {
                statusCode = 429,
                message = "Too many requests. Please try again later."
            },
            cancellationToken);
    };

    options.AddFixedWindowLimiter(
        policyName: "FixedPolicy",
        configureOptions: limiterOptions =>
        {
            limiterOptions.PermitLimit = 5;
            limiterOptions.Window =
                TimeSpan.FromSeconds(10);

            limiterOptions.QueueLimit = 0;
            limiterOptions.QueueProcessingOrder =
                QueueProcessingOrder.OldestFirst;
        });
});

Response after exceeding the limit:

{
  "statusCode": 429,
  "message": "Too many requests. Please try again later."
}

Disable rate limiting for an action

An endpoint can be excluded using [DisableRateLimiting]:

[HttpGet("health")]
[DisableRateLimiting]
public IActionResult Health()
{
    return Ok("API is healthy.");
}

This can be useful for selected health-check or internal endpoints, but exclusions should be chosen carefully.


Important limitation of the simple example

The named fixed-window policy shown above uses a shared limit for the protected endpoint. In most real applications, limits should be separated by:

  • User ID

  • Client IP address

  • API key

  • Subscription plan

Otherwise, one busy client could consume the request quota and affect other clients.

For example:

User A → 100 requests per minute
User B → Separate 100 requests per minute
User C → Separate 100 requests per minute

This is called partitioned rate limiting.


Rate-limiting algorithms

ASP.NET Core provides four main algorithms:

Algorithm Purpose
Fixed window Allows a fixed number of requests during fixed time blocks
Sliding window Provides smoother control by dividing the window into segments
Token bucket Permits controlled traffic bursts while tokens are available
Concurrency Limits requests executing at the same time

For a basic Web API, fixed window is the easiest starting point.

Key points

  • Rate limiting controls incoming API traffic.

  • Rejected requests normally receive 429 Too Many Requests.

  • Register services using AddRateLimiter().

  • Enable middleware using UseRateLimiter().

  • Create a policy such as "FixedPolicy".

  • Apply it with [EnableRateLimiting] or RequireRateLimiting().

  • Use [DisableRateLimiting] for carefully selected exceptions.

  • Prefer per-user, per-IP or per-API-key limits in production.

  • Load-test the chosen limits before deploying.

  • Rate limiting supports security but does not replace proper DDoS protection.