Skip to main content

Rate Limiter Strategy

The rate limiter proactive resilience strategy controls the number of operations that can pass through it. This strategy is built on top of the System.Threading.RateLimiting API.
The rate limiter strategy resides in the Polly.RateLimiting package, not in Polly.Core like other strategies.

When to Use Rate Limiter

Use the rate limiter strategy when:
  • Protecting your API from being overwhelmed by too many requests
  • Implementing fair usage policies across multiple clients
  • Controlling outbound calls to rate-limited external APIs
  • Preventing resource exhaustion from excessive concurrent operations
  • Implementing token bucket, sliding window, or fixed window rate limiting

Installation

Rate Limiter Types

Polly supports several rate limiter types:
  • Concurrency Limiter: Limits the number of concurrent operations
  • Fixed Window: Allows a fixed number of operations per time window
  • Sliding Window: Smooths rate limiting across overlapping time segments
  • Token Bucket: Uses tokens that regenerate over time
  • Partitioned: Different rate limits per key (e.g., per user)

Usage

Basic Concurrency Limiter

Sliding Window Rate Limiter

Fixed Window Rate Limiter

Token Bucket Rate Limiter

With OnRejected Event

Configuration Options

Func<OnRateLimiterArguments, ValueTask<RateLimitLease>>
default:"null"
Dynamically creates a RateLimitLease for executions. Allows for custom rate limiting logic.
ConcurrencyLimiterOptions
default:"PermitLimit: 1000, QueueLimit: 0"
If RateLimiter is not provided, uses these options for the default concurrency limiter.
Func<OnRateLimiterRejectedArguments, ValueTask>
default:"null"
Invoked after the limiter rejected an execution, before throwing RateLimiterRejectedException.

Advanced Scenarios

Partitioned Rate Limiter (Per-User)

Chained Rate Limiters

Combine multiple rate limiters for complex scenarios:

Custom Rate Limiter Logic

Proper Disposal

When using dynamic reloads or manual rate limiter creation, ensure proper disposal:

Best Practices

  • Concurrency Limiter: Limit simultaneous operations (database connections, API calls)
  • Fixed Window: Simple rate limiting with defined time windows
  • Sliding Window: Smoother rate limiting, prevents burst at window edges
  • Token Bucket: Allows bursts while maintaining average rate
  • Partitioned: Different limits per user/tenant/key
Set QueueLimit based on acceptable latency:
  • 0: Reject immediately when limit reached
  • Small value: Low latency, but more rejections
  • Large value: Fewer rejections, but higher latency
Prevent one user/tenant from consuming all resources:
When catching RateLimiterRejectedException, check for RetryAfter and respect it:
Use OnRejected to track rejections and adjust limits:
Rate limiter is for controlling load, not for implementing retry delays. Use the Retry strategy for that.

Examples

API Rate Limiting Middleware

External API Client with Rate Limiting

Database Connection Pool Management

  • Concurrency Limiter: Limits simultaneous operations (e.g., max 10 concurrent requests)
  • Rate Limiter: Limits operations over time (e.g., max 100 requests per minute)
Use concurrency limiter for resource protection, rate limiter for throughput control.
Yes! You can either:
  1. Chain multiple AddRateLimiter calls (they’ll be applied in sequence)
  2. Use PartitionedRateLimiter.CreateChained to combine multiple limiters
Use PartitionedRateLimiter with the user ID as the partition key. Store the user ID in the ResilienceContext.Properties and extract it in the partition selector.
No. Rate limiter is for controlling load and preventing overuse. Use the Retry strategy’s delay configuration for retry timing.