Skip to main content

Rate Limiter Policy

The rate limiter policy controls how many requests can be made within a time window, protecting downstream services from being overwhelmed.

Algorithms

AlgorithmDescription
TokenBucketA bucket fills with tokens over time; each request consumes one token
SlidingWindowCounts requests in a sliding time window

Token Bucket

var policy = ResiliencePolicy.Create()
.RateLimiter(opts =>
{
opts.Algorithm = RateLimiterAlgorithm.TokenBucket;
opts.BucketCapacity = 100; // max burst
opts.TokensPerInterval = 10; // replenishment rate
opts.ReplenishmentInterval = TimeSpan.FromSeconds(1); // every second
})
.Build();

Shorthand (uses token bucket with defaults):

var policy = ResiliencePolicy.Create()
.RateLimiter(bucketCapacity: 100)
.Build();

Token Bucket Behavior

  • Starts full (100 tokens)
  • Each request consumes 1 token
  • Every second, 10 tokens are added (up to 100 max)
  • When bucket is empty → reject

Sliding Window

var policy = ResiliencePolicy.Create()
.RateLimiter(opts =>
{
opts.Algorithm = RateLimiterAlgorithm.SlidingWindow;
opts.PermitLimit = 100; // max requests per window
opts.Window = TimeSpan.FromMinutes(1); // window duration
})
.Build();

Rejection Handling

When the limit is exceeded, a RateLimitExceededException is thrown:

try
{
var result = await policy.ExecuteAsync<string>(async ct =>
await _api.CallAsync(ct));
}
catch (RateLimitExceededException ex)
{
_logger.LogWarning(
"Rate limit exceeded (Algorithm: {Algorithm})",
ex.Algorithm); // "TokenBucket" or "SlidingWindow"

// Return 429 Too Many Requests
return Results.StatusCode(429);
}

OnRejected Callback

.RateLimiter(opts =>
{
opts.Algorithm = RateLimiterAlgorithm.TokenBucket;
opts.BucketCapacity = 50;
opts.OnRejected = context =>
{
_metrics.IncrementRateLimitHit();
return Task.CompletedTask;
};
})

Use Cases

Outbound API Rate Limiting

Respect a third-party API's rate limits:

// GitHub API: 5000 requests/hour
var githubPolicy = ResiliencePolicy.Create()
.RateLimiter(opts =>
{
opts.Algorithm = RateLimiterAlgorithm.SlidingWindow;
opts.PermitLimit = 5000;
opts.Window = TimeSpan.FromHours(1);
})
.Build();

Per-User Request Throttling

Create one policy instance per user/tenant:

var userPolicies = new ConcurrentDictionary<string, ResiliencePolicy>();

ResiliencePolicy GetUserPolicy(string userId) =>
userPolicies.GetOrAdd(userId, _ => ResiliencePolicy.Create()
.RateLimiter(opts =>
{
opts.Algorithm = RateLimiterAlgorithm.SlidingWindow;
opts.PermitLimit = 10;
opts.Window = TimeSpan.FromSeconds(1);
})
.Build());