Rate Limiting and Throttling in C#: Taming Request Streams

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Rate Limiting and Throttling in C#: How to Protect Your API from Overloads and Keep Users Happy

\n\n

Imagine: you've launched a cool API. The server is flying, the database responds instantly. But suddenly — bam! — one careless client (or, even worse, a DDoS attack) starts hammering your endpoint with thousands of requests per second. The server crashes, the database chokes, and real users see a 503 error. Sound familiar?

\n\n

The solution to this problem is rate limiting and throttling. In C#, we have all the tools to implement these mechanisms elegantly and efficiently. In this article, we won't just go through the theory — we'll write real code, break down popular algorithms, and figure out what to choose for a specific task. Buckle up, it's going to be intense!

\n\n

Rate Limiting vs Throttling: What's the Difference?

\n\n

Although these terms are often confused, there is a clear line between them:

\n
    \n
  • Rate Limiting is a "hard" block. If a user exceeds the limit (e.g., 100 requests per minute), the server returns HTTP 429 (Too Many Requests). Requests simply don't reach the business logic.
  • \n
  • Throttling is a "soft" slowdown. The server accepts the request but processes it with a delay or lowers its priority. The user doesn't get an error, but their speed drops.
  • \n
\n

In C#, we can implement both approaches, often combining them for different scenarios.

\n\n

Rate Limiting Algorithms: From Simple to Complex

\n\n

Before writing code, let's understand the math. There are four "golden" algorithms that form the basis of 90% of solutions.

\n\n

1. Fixed Window

\n

The simplest way. We divide time into fixed intervals (e.g., 1 minute) and count requests in each window. Once the counter reaches the limit — we block.

\n

Pros: Simplicity of implementation. Cons: The "edge effect" problem (burst at the window boundary).

\n\n
public class FixedWindowRateLimiter\n{\n    private readonly int _limit;\n    private readonly TimeSpan _windowSize;\n    private int _counter;\n    private DateTime _windowStart;\n\n    public FixedWindowRateLimiter(int limit, TimeSpan windowSize)\n    {\n        _limit = limit;\n        _windowSize = windowSize;\n        _windowStart = DateTime.UtcNow;\n    }\n\n    public bool AllowRequest()\n    {\n        var now = DateTime.UtcNow;\n        if (now - _windowStart >= _windowSize)\n        {\n            // Reset window\n            _counter = 0;\n            _windowStart = now;\n        }\n\n        if (_counter >= _limit)\n            return false; // Too Many Requests\n\n        Interlocked.Increment(ref _counter);\n        return true;\n    }\n}
\n\n

2. Sliding Window

\n

An evolution of Fixed Window. Instead of rigid boundaries, we account for requests in a sliding time segment. It is usually implemented by logging timestamps (e.g., in a circular buffer).

\n

Pros: Smooths out bursts. Cons: Slightly more complex to implement, requires memory to store timestamps.

\n\n
public class SlidingWindowRateLimiter\n{\n    private readonly int _limit;\n    private readonly TimeSpan _windowSize;\n    private readonly Queue<DateTime> _timestamps = new();\n\n    public SlidingWindowRateLimiter(int limit, TimeSpan windowSize)\n    {\n        _limit = limit;\n        _windowSize = windowSize;\n    }\n\n    public bool AllowRequest()\n    {\n        var now = DateTime.UtcNow;\n        lock (_timestamps)\n        {\n            // Remove outdated timestamps\n            while (_timestamps.Count > 0 && now - _timestamps.Peek() > _windowSize)\n                _timestamps.Dequeue();\n\n            if (_timestamps.Count >= _limit)\n                return false;\n\n            _timestamps.Enqueue(now);\n            return true                

Blogs

Book Recommendations