All four built-in rate limiting algorithms in one place — Fixed Window, Sliding Window, Token Bucket, and Concurrency Limiter with real endpoints, integration tests, and HTTP 429 responses done right.
If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content — all for the price of a coffee.
👉 Join CodingDroplets on Patreon
Prefer a one-time tip? Buy us a coffee ☕
- How to configure all four built-in rate limiting algorithms in ASP.NET Core
- How to apply policies per-controller or per-action with
[EnableRateLimiting] - How to exempt specific endpoints using
[DisableRateLimiting] - How to return structured HTTP 429 Too Many Requests responses with
Retry-Afterheaders - How to use the modern Scalar UI (OpenAPI) for interactive API exploration
- How to write integration tests that exercise rate-limited endpoints
Incoming HTTP Request
│
▼
┌──────────────────────────────────────────────────────┐
│ ASP.NET Core Middleware Pipeline │
│ ┌────────────────────────────────────────────────┐ │
│ │ app.UseRateLimiter() ← FIRST │ │
│ │ Checks policy for this endpoint │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Within limit? │ │ │
│ │ │ YES → forward to controller │ │ │
│ │ │ NO → OnRejected → HTTP 429 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ Controllers annotated with [EnableRateLimiting("x")]│
└──────────────────────────────────────────────────────┘
│ │
▼ ▼
200 OK Response 429 Too Many Requests
+ Retry-After header
+ Problem Details JSON
| Algorithm | Policy Name | Config | Best For |
|---|---|---|---|
| Fixed Window | fixed |
5 req / 10 s | Simple API throttling |
| Sliding Window | sliding |
10 req / 20 s (4 segments) | Smooth burst control |
| Token Bucket | token |
8 tokens, refill 2 / 5 s | Burst + steady average |
| Concurrency | concurrency |
Max 3 concurrent | Protecting slow resources |
dotnet-rate-limiting-api/
├── dotnet-rate-limiting-api.sln
├── RateLimitingApi/
│ ├── Controllers/
│ │ ├── FixedWindowController.cs ← Fixed Window demo
│ │ ├── SlidingWindowController.cs ← Sliding Window demo
│ │ ├── TokenBucketController.cs ← Token Bucket demo
│ │ ├── ConcurrencyController.cs ← Concurrency Limiter demo
│ │ └── NoLimitController.cs ← [DisableRateLimiting] demo
│ ├── Models/
│ │ ├── ApiResponse.cs ← Generic response envelope
│ │ └── Product.cs ← Sample domain model
│ ├── Properties/
│ │ └── launchSettings.json
│ └── Program.cs ← All rate limiters registered here
└── RateLimitingApi.Tests/
└── RateLimitingIntegrationTests.cs ← 16 integration tests
- .NET 10 SDK
- Any IDE: Visual Studio 2022+, VS Code, or JetBrains Rider
# Clone the repo
git clone https://github.com/codingdroplets/dotnet-rate-limiting-api.git
cd dotnet-rate-limiting-api
# Build
dotnet build -c Release
# Run the API
cd RateLimitingApi
dotnet run
# Open Scalar UI → http://localhost:5289/scalar/v1builder.Services.AddRateLimiter(options =>
{
// 1. Fixed Window — 5 requests per 10 seconds
options.AddFixedWindowLimiter(policyName: "fixed", opt =>
{
opt.PermitLimit = 5;
opt.Window = TimeSpan.FromSeconds(10);
opt.QueueLimit = 2;
});
// 2. Sliding Window — 10 requests per 20 seconds (4 segments)
options.AddSlidingWindowLimiter(policyName: "sliding", opt =>
{
opt.PermitLimit = 10;
opt.Window = TimeSpan.FromSeconds(20);
opt.SegmentsPerWindow = 4;
opt.QueueLimit = 2;
});
// 3. Token Bucket — 8 tokens, replenish 2 every 5 seconds
options.AddTokenBucketLimiter(policyName: "token", opt =>
{
opt.TokenLimit = 8;
opt.ReplenishmentPeriod = TimeSpan.FromSeconds(5);
opt.TokensPerPeriod = 2;
opt.AutoReplenishment = true;
opt.QueueLimit = 2;
});
// 4. Concurrency — max 3 simultaneous requests
options.AddConcurrencyLimiter(policyName: "concurrency", opt =>
{
opt.PermitLimit = 3;
opt.QueueLimit = 2;
});
// Custom 429 response with Retry-After header
options.OnRejected = async (context, ct) =>
{
context.HttpContext.Response.StatusCode = 429;
context.HttpContext.Response.Headers["Retry-After"] = "10";
await context.HttpContext.Response.WriteAsJsonAsync(new
{
status = 429,
error = "Too Many Requests",
message = "Rate limit exceeded. Please wait before retrying.",
retryAfterSeconds = 10
}, cancellationToken: ct);
};
});
// IMPORTANT: UseRateLimiter() must come before MapControllers()
app.UseRateLimiter();// Apply a named policy to all actions in this controller
[EnableRateLimiting("fixed")]
public class FixedWindowController : ControllerBase { ... }
// Exempt an endpoint from all rate limiting
[DisableRateLimiting]
public class NoLimitController : ControllerBase { ... }| Method | Endpoint | Description |
|---|---|---|
GET |
/api/nolimit/status |
API status & policy overview |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/fixedwindow/products |
List all products |
GET |
/api/fixedwindow/products/{id} |
Get product by ID |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/slidingwindow/orders |
List all orders |
GET |
/api/slidingwindow/orders/{id} |
Get order by ID |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/tokenbucket/reports |
List all reports |
GET |
/api/tokenbucket/reports/{id} |
Get report by ID |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/concurrency/inventory |
List inventory (simulated slow query) |
GET |
/api/concurrency/inventory/{id} |
Get inventory item by ID |
dotnet test -c Release16 integration tests cover all five controllers:
| Area | Coverage |
|---|---|
| Happy path | 200 OK for all controllers |
| Not found | 404 for all controllers |
| JSON content-type | Validated on all responses |
| Response envelope | success: true structure verified |
| Use Case | Best Algorithm |
|---|---|
| Simple API key / per-IP throttle | Fixed Window |
| Smooth traffic shaping, no boundary spikes | Sliding Window |
| Allow short bursts, enforce an average rate | Token Bucket |
| Protect expensive DB queries / third-party APIs | Concurrency |
Once running, open Scalar UI in your browser:
http://localhost:5289/scalar/v1
OpenAPI JSON spec:
http://localhost:5289/openapi/v1.json
This project is licensed under the MIT License.
| Platform | Link |
|---|---|
| 🌐 Website | https://codingdroplets.com/ |
| 📺 YouTube | https://www.youtube.com/@CodingDroplets |
| 🎁 Patreon | https://www.patreon.com/CodingDroplets |
| ☕ Buy Me a Coffee | https://buymeacoffee.com/codingdroplets |
| 💻 GitHub | http://github.com/codingdroplets/ |
Want more samples like this? Support us on Patreon or buy us a coffee ☕ — every bit helps keep the content coming!