A scalable, plug-and-play API rate limiting library built in .NET, supporting multiple algorithms like Fixed Window and Token Bucket, with both in-memory and Redis-based distributed execution.
Designed with clean architecture, extensibility, and real-world production patterns in mind.
Modern APIs must handle unpredictable traffic, prevent abuse, and ensure fair usage across clients. This library provides a modular and extensible rate limiting solution that can be easily integrated into any .NET application using middleware.
It separates:
- Decision-making (rate limiting)
- Configuration (rules)
- Execution (algorithms)
Without rate limiting:
- APIs can be overwhelmed by traffic spikes
- Malicious users can abuse endpoints
- Backend systems can crash under load
- No fairness between users
With rate limiting:
- Protects system stability
- Prevents abuse (DDoS / brute force)
- Ensures fair usage
- Controls traffic flow
- Plug-and-play middleware integration
- Multiple algorithms:
- Fixed Window
- Token Bucket
- Distributed support using Redis
- Lua scripting for atomic operations
- Dynamic rule updates (no restart required)
- Clean, extensible architecture
Responsibility:
Main interface used by middleware to decide whether a request is allowed.
Function:
- Receives request
- Delegates to engine
- Returns allow/block decision
Responsibility:
Central dispatcher that selects the correct algorithm dynamically.
Function:
- Resolves applicable rule
- Chooses strategy (Fixed Window / Token Bucket)
- Executes rate limiting logic
Implementations:
- FixedWindowRateLimiter
- TokenBucketRateLimiter
- Limits requests within a fixed time window
- Simple and efficient
- Example: 5 requests per minute
- Allows burst traffic
- Smooth refill over time
- Example: capacity = 5, refill = 1/sec
Responsibility:
Defines configuration for rate limiting.
Fields:
- RuleId
- Algorithm type
- Limit / Window OR Capacity / RefillRate
- Endpoint
Responsibility:
Stores and provides rules.
Current Implementation:
- In-memory provider
- Supports dynamic updates
Responsibility:
Maps incoming request → applicable rule.
Function:
- Matches request endpoint
- Returns correct rule
Responsibility:
Handles storage of counters/tokens.
Implementations:
- Fast, local
- Used for testing
- Distributed
- Uses Lua scripts for atomic operations
Builds keys like: clientId:ruleId:timeWindow
Builds:
- tokens key
- timestamp key
Responsibility:
Intercepts every request and enforces rate limiting.
Flow:
- Extract client info
- Call rate limiter
- Block or allow request
Responsibility:
Allows dynamic updates of rules.
Features:
- Update Fixed Window rules
- Update Token Bucket rules
- No application restart required
This project supports Redis for distributed rate limiting. You can use any Redis setup (local install, cloud, or cluster).
docker run -d -p 6379:6379 --name redis-rate-limiter redis- Request enters middleware
- Middleware builds
RateLimitRequest - Engine resolves rule
- Engine selects strategy
- Strategy checks store
- Decision returned:
- Allowed
- Blocked (429 Too Many Requests)
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowRule(
ruleId: "default",
endpoint: "/api/test",
limit: 5,
window: TimeSpan.FromMinutes(1)
);
options.AddTokenBucketRule(
ruleId: "login",
endpoint: "/api/login",
capacity: 2,
refillRate: 1 // tokens per second
);
});- Multi-algorithm support
- Distributed rate limiting
- Dynamic configuration
- Middleware-based enforcement
- Sliding Window algorithm
- Leaky Bucket algorithm
- Per-user / API key rate limiting
- Tier-based throttling (Free vs Premium users)
- Metrics & analytics integration
- Persistent rule storage (DB/config service)
- Integration with monitoring tools (Prometheus, Grafana)
- Circuit breaker integration
- Strategy Pattern
- Dependency Injection
- Interface Segregation Principle
- Separation of Concerns
- Control Plane vs Data Plane
Pros:
- Simple to implement and understand
- Low latency and minimal computation
Cons:
- Allows burst traffic at window boundaries (e.g., double requests at edge of time window)
- Less accurate for smoothing traffic
Pros:
- Handles burst traffic efficiently
- Provides smoother request distribution over time
- Better user experience under fluctuating load
Cons:
- Slightly more complex to implement and maintain state
- Requires careful tuning of refill rates
Pros:
- Ensures atomic operations in distributed environments
- Prevents race conditions across multiple instances
- Reduces number of network round trips
Cons:
- Adds operational dependency on Redis
- Increased system complexity
- Requires handling Redis failure scenarios
- Supports fallback strategies (e.g., in-memory rate limiting)
- Configurable behavior:
- Fail-Open: Allow requests when Redis is unavailable (better availability)
- Fail-Closed: Block requests when Redis is unavailable (better protection)
- Minimized Redis interactions using Lua scripts
- Atomic operations reduce multiple round trips
- Designed to keep request decision latency low
- Eliminated using Redis Lua scripting for atomic updates
- Ensures consistent rate-limiting decisions across distributed instances
- Stateless middleware enables easy horizontal scaling across multiple application instances
- Redis acts as a centralized coordination layer for distributed rate limiting
- Key design ensures proper isolation (e.g., per client, per endpoint, per rule)
- Can be extended to Redis Cluster for high throughput and fault tolerance
- Designed to handle high concurrency with minimal contention
If you find this useful, consider giving it a ⭐ on GitHub!
