Skip to content

Rate Limiting — Sliding Window & Token Bucket

Granit.RateLimiting provides per-tenant rate limiting with four algorithms, configurable policies, Redis-backed counters (with in-memory fallback), and Wolverine message handler support. Integrates with Granit.Features for plan-based dynamic quotas.

[DependsOn(typeof(GranitRateLimitingModule))]
public class AppModule : GranitModule { }
{
"RateLimiting": {
"Enabled": true,
"KeyPrefix": "rl",
"BypassRoles": ["admin"],
"FallbackOnCounterStoreFailure": "Deny",
"Policies": {
"api-default": {
"Algorithm": "SlidingWindow",
"PermitLimit": 1000,
"Window": "00:01:00",
"SegmentsPerWindow": 6
},
"api-sensitive": {
"Algorithm": "TokenBucket",
"TokenLimit": 50,
"TokensPerPeriod": 10,
"ReplenishmentPeriod": "00:00:10"
}
}
}
}
app.MapGet("/api/v1/appointments", GetAppointments)
.RequireGranitRateLimiting("api-default");
app.MapPost("/api/v1/payments", ProcessPayment)
.RequireGranitRateLimiting("api-sensitive");

Decorate message types with [RateLimited] and register the Wolverine middleware:

[RateLimited("api-default")]
public record SyncPatientCommand(Guid PatientId);
// In Wolverine configuration
opts.Policies.AddMiddleware<RateLimitMiddleware>(
chain => chain.MessageType.GetCustomAttributes(typeof(RateLimitedAttribute), true).Length > 0);

When the rate limit is exceeded, RateLimitExceededException is thrown and handled by Wolverine’s retry policy.

AlgorithmUse caseKey parameters
SlidingWindowGeneral API rate limiting (default)PermitLimit, Window, SegmentsPerWindow
FixedWindowSimple counter, lowest memoryPermitLimit, Window
TokenBucketControlled burst allowanceTokenLimit, TokensPerPeriod, ReplenishmentPeriod
ConcurrencyLimit simultaneous in-flight requestsPermitLimit

Rate limit counters are partitioned according to PartitionBy on each policy. Redis keys use hash tags for Cluster slot co-location.

PartitionByKey formatUse case
Tenant (default)rl:{tenantId}:policyShared tenant quota
TenantAndIprl:{tenantId}:ip:policyPer-IP within a tenant
Iprl:{ip}:policyUnauthenticated endpoints (login, reset)
Userrl:{userId}:policyPer-user quota
TenantAndUserrl:{tenantId}:userId:policyPer-user within a tenant
{
"Policies": {
"auth": {
"Algorithm": "FixedWindow",
"PermitLimit": 5,
"Window": "00:15:00",
"PartitionBy": "Ip"
}
}
}

When Redis is unavailable, the FallbackOnCounterStoreFailure setting controls behavior:

ValueBehaviorUse case
AllowLet the request through, log warningPrefer availability over quota enforcement
DenyReject with 429Conservative — prefer safety over availability

On allowed responses, the following headers are set:

HeaderDescription
X-RateLimit-LimitTotal permit limit for the policy
X-RateLimit-RemainingRemaining permits in the current window

On rejected responses (429), Retry-After is set in addition.

{
"status": 429,
"title": "Too Many Requests",
"detail": "Too many requests. Please retry later.",
"limit": 1000,
"remaining": 0,
"retryAfter": 10
}

When UseFeatureBasedQuotas is enabled, the permit limit is resolved dynamically from Granit.Features (e.g., per-plan quotas). The convention-based feature name is RateLimit.{PolicyName}, overridable via RateLimitPolicyOptions.FeatureName.

PropertyDefaultDescription
EnabledtrueEnable/disable rate limiting globally
KeyPrefix"rl"Redis key prefix
FallbackOnCounterStoreFailureDenyBehavior when Redis is down
BypassRoles[]Roles that skip rate limiting
UseFeatureBasedQuotasfalseUse Granit.Features for dynamic quotas
Policies.*Named rate limiting policies (see below)

Policy options:

PropertyDefaultDescription
AlgorithmSlidingWindowRate limiting algorithm
PartitionByTenantKey partitioning strategy (Tenant, TenantAndIp, Ip, User, TenantAndUser)
PermitLimit1000Max permits per window
Window00:01:00Window duration
SegmentsPerWindow6Sliding window segments (accuracy vs. memory)
TokenLimit50Max tokens (TokenBucket only)
TokensPerPeriod10Tokens added per replenishment (TokenBucket only)
ReplenishmentPeriod00:00:10Replenishment interval (TokenBucket only)
FeatureNamenullOverride feature name for dynamic quotas
CategoryKey typesPackage
ModuleGranitRateLimitingModule
StoreIRateLimitCounterStore, IRateLimitQuotaProvider, RateLimitResultGranit.RateLimiting
AttributesRateLimitedAttributeGranit.RateLimiting
ExceptionsRateLimitExceededExceptionGranit.RateLimiting
OptionsGranitRateLimitingOptions, RateLimitPolicyOptions, RateLimitPartitionGranit.RateLimiting
ExtensionsAddGranitRateLimiting(), .RequireGranitRateLimiting()Granit.RateLimiting