Other 4xx codes
All HTTP status codes in ASP.NET Core
4xx Client error
The client sent too many requests in a given time (RFC 6585). A Retry-After header may say when to try again.
In ASP.NET Core: The code clients expect from rate limiting, but ASP.NET Core's rate limiter sends 503 unless you set RejectionStatusCode.
The StatusCodes constant, HttpStatusCode enum name, reason phrase, class, and how EnsureSuccessStatusCode() and the standard resilience handler treat 429.
| Constant | StatusCodes.Status429TooManyRequests |
|---|---|
| HttpStatusCode | HttpStatusCode.TooManyRequests |
| Reason phrase | Kestrel sends Too Many Requests |
| Class | 4xx, client error |
| IsSuccessStatusCode | false |
| EnsureSuccessStatusCode() | throws HttpRequestException: Response status code does not indicate success: 429 (Too Many Requests). |
| Standard resilience handler | retries it (treated as transient) |
Results.StatusCode(StatusCodes.Status429TooManyRequests)
Results.Problem(statusCode: StatusCodes.Status429TooManyRequests, detail: "...")StatusCode(StatusCodes.Status429TooManyRequests)
Problem(statusCode: StatusCodes.Status429TooManyRequests, detail: "...")What Results.Problem(statusCode: 429) sends (with AddProblemDetails()), as application/problem+json:
{
"title": "Too Many Requests",
"status": 429,
"traceId": "0HNA1B2C3D4E5:00000001"
}A controller's Problem(statusCode: 429) sends a different body, because MVC only fills type and title for codes in ApiBehaviorOptions.ClientErrorMapping:
{
"status": 429,
"traceId": "0HNA1B2C3D4E5:00000001"
}A bare StatusCode(429) in an [ApiController] also gets a ProblemDetails body automatically; Results.StatusCode(429) in a minimal API sends an empty body unless you add UseStatusCodePages().
Setup
builder.Services.AddRateLimiter(o =>
{
o.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
o.AddFixedWindowLimiter("one", w => { w.PermitLimit = 1; w.Window = TimeSpan.FromMinutes(1); });
});
app.UseRateLimiter();
app.MapGet("/limited", () => "ok").RequireRateLimiting("one");Request
GET /limited HTTP/1.1 (second request in the window)Response (recorded)
HTTP/1.1 429 Too Many RequestsWith RejectionStatusCode set, rejected requests get 429. No Retry-After header is added unless you write one in OnRejected.
Setup
builder.Services.AddRateLimiter(o =>
o.AddFixedWindowLimiter("one", w => { w.PermitLimit = 1; w.Window = TimeSpan.FromMinutes(1); }));
app.UseRateLimiter();Request
GET /limited HTTP/1.1 (second request in the window)Response (recorded)
HTTP/1.1 503 Service UnavailableRateLimiterOptions.RejectionStatusCode defaults to 503, not 429. Clients and HttpClient's resilience handler treat 503 as a server problem.
Fix: Set o.RejectionStatusCode = StatusCodes.Status429TooManyRequests, and add Retry-After in OnRejected.
Every response on this page was recorded from ASP.NET Core 10.0.12 on Kestrel in the Production environment; the HttpClient rows come from .NET 10.0.12 and Microsoft.Extensions.Http.Resilience 10.10.0.
All HTTP status codes in ASP.NET Core