<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rate-Limiter · chaewonkong</title><link>https://blog.engineerd.net/en/tags/rate-limiter/</link><description>Notes on matchmaking and distributed systems.</description><language>en</language><managingEditor>chaewonkong@gmail.com (Chaewon Kong)</managingEditor><lastBuildDate>Tue, 29 Sep 2026 21:32:00 +0900</lastBuildDate><atom:link href="https://blog.engineerd.net/en/tags/rate-limiter/index.xml" rel="self" type="application/rss+xml"/><item><title>Rate Limiter Algorithms</title><link>https://blog.engineerd.net/en/p/rate-limit/</link><pubDate>Tue, 29 Sep 2026 21:32:00 +0900</pubDate><guid>https://blog.engineerd.net/en/p/rate-limit/</guid><description>&lt;p&gt;Servers and databases are finite. When requests pile up or spike, something has to control the inflow before the load takes those resources down.&lt;/p&gt;
&lt;p&gt;That is what a rate limiter does: it is a component that controls the rate of traffic sent by a client or a service.&lt;/p&gt;
&lt;h2 id="use-cases"&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;Rate limiters serve many purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capping daily requests on a free public API to keep operating costs down&lt;/li&gt;
&lt;li&gt;Limiting login attempts per user per minute to prevent brute-force attacks&lt;/li&gt;
&lt;li&gt;Limiting the number of client requests to defend against malicious requests or DoS attacks&lt;/li&gt;
&lt;li&gt;Throttling a ticketing system when traffic surges on the day sales open, so the booking service doesn&amp;rsquo;t fall over&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The purposes vary, and protecting server-side resources is only one of them. What a rate limiter does, in the end, is control how many requests are allowed per unit of time.&lt;/p&gt;
&lt;h2 id="algorithms"&gt;Algorithms&lt;/h2&gt;
&lt;p&gt;So how do we count requests, and how do we cap them? The commonly used rate limiting algorithms are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Token Bucket&lt;/li&gt;
&lt;li&gt;Leaky Bucket&lt;/li&gt;
&lt;li&gt;Fixed Window Counter&lt;/li&gt;
&lt;li&gt;Sliding Window Log&lt;/li&gt;
&lt;li&gt;Sliding Window Counter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each algorithm has its own pros and cons, and each fits different situations.
Let&amp;rsquo;s walk through them, assuming a system that limits requests per IP.&lt;/p&gt;
&lt;h3 id="token-bucket"&gt;Token Bucket&lt;/h3&gt;
&lt;p&gt;There is a bucket, and tokens are added to it at a fixed rate. Once the bucket is full, no more tokens are added.
When a request comes in, it takes one token from the bucket and passes through. If the bucket is empty, the request is dropped.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; refill: r tokens/sec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────┐ overflow (bucket full) → token discarded
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ● ● ● ● │ ◀─ capacity = b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ● ● ● │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ● ● │ tokens = currently remaining
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────┬────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 1 request = 1 token consumed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; request ──▶ has token? ──yes──▶ pass
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; no ──▶ reject (429)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="characteristics"&gt;Characteristics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Allows bursts up to what has accumulated in the bucket (at most b requests at once)&lt;/li&gt;
&lt;li&gt;The sustained rate is capped at r&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pros"&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Simple to implement&lt;/li&gt;
&lt;li&gt;Memory efficient&lt;/li&gt;
&lt;li&gt;Handles bursts of traffic (as long as there are tokens in the bucket)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="cons"&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Tuning its two parameters, bucket size and refill rate, is tricky.
&lt;ul&gt;
&lt;li&gt;How much burst should we allow?&lt;/li&gt;
&lt;li&gt;How many requests should we allow per unit of time?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="leaky-bucket"&gt;Leaky Bucket&lt;/h3&gt;
&lt;p&gt;The processing rate is fixed. Requests arrive at a queue; if there is room, they are enqueued, otherwise they are dropped. On every tick, requests are dequeued and processed at the fixed rate. In other words, the server decides how many requests it can handle per unit of time and processes at most that many.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; requests (irregular, bursty)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼ ▼ ▼ ▼ ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ▒▒▒▒▒▒▒▒▒▒▒▒▒ │ ◀─ new requests rejected when queue is full
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ▒▒▒▒▒▒▒▒▒ │ (capacity = queue size)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ▒▒▒▒ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────┬───────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ leaks at a constant rate (leak rate = r)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ─ ─ ─ ─ ─ ─ uniform output (burst smoothed out)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="characteristics-1"&gt;Characteristics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Output never exceeds r&lt;/li&gt;
&lt;li&gt;Absorbs bursts, but delays them instead of letting them through&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pros-1"&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Memory is bounded by the queue size&lt;/li&gt;
&lt;li&gt;The fixed processing rate suits cases where a stable output rate matters&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="cons-1"&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;When traffic spikes briefly, the most recent requests get dropped, and the ones that made it into the queue wait, adding latency&lt;/li&gt;
&lt;li&gt;Tuning its parameters, bucket (queue) size and processing rate, is tricky&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="note-the-meter-variant"&gt;Note: the meter variant&lt;/h4&gt;
&lt;p&gt;Note that besides the queue-based version described here, Leaky Bucket can also be implemented as a meter. The meter version doesn&amp;rsquo;t queue requests; it only tracks the bucket&amp;rsquo;s water level (a counter). Each request raises the level by 1, and the level drains at a constant rate (r) over time. If the level would exceed capacity, the request is rejected. Since requests are passed or rejected immediately instead of being delayed, it effectively behaves the same as a Token Bucket. (NGINX&amp;rsquo;s &lt;code&gt;limit_req&lt;/code&gt; works this way, and its &lt;code&gt;burst&lt;/code&gt;/&lt;code&gt;delay&lt;/code&gt; options let it delay requests like a queue.)&lt;/p&gt;
&lt;h3 id="fixed-window-counter"&gt;Fixed Window Counter&lt;/h3&gt;
&lt;p&gt;Split the timeline into fixed-size windows and keep a counter for each window.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; limit = 5 / window (e.g. 1 min)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; window 1 window 2 window 3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────────────┬─────────────────┬─────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ● ● ● ● ● ✕ ✕ │ ● ● │ ● ● ● ● │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ count=5 (full) │ count=2 │ count=4 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─────────────────┴─────────────────┴─────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 00:00 01:00 02:00 03:00
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▲
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └ counter reset to 0 at the boundary
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Around window boundaries, more requests than the limit can get through within a span of one window size.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Problem: bursts on both sides of a boundary let 2×limit through
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌────────────┬────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ●●●●●│●●●●● │ ← 10 requests pass between 00:59 and 01:01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────────────┴────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="characteristics-2"&gt;Characteristics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The simplest to implement (one window key + one counter; Redis INCR + EXPIRE and you&amp;rsquo;re done)&lt;/li&gt;
&lt;li&gt;O(1) memory, O(1) operations; in a distributed setup, only a single counter needs to be shared&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pros-2"&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Memory efficient&lt;/li&gt;
&lt;li&gt;Easy to understand&lt;/li&gt;
&lt;li&gt;A good fit when resetting the counter at the end of a window matches the traffic pattern, e.g. resetting the purchase count for an item limited to 5 per day&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="cons-2"&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;When traffic concentrates around window boundaries, the system ends up handling more requests than intended.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sliding-window-log"&gt;Sliding Window Log&lt;/h3&gt;
&lt;p&gt;Track the timestamp of each request. When a new request arrives, remove the timestamps that have expired out of the window, then append the new request&amp;rsquo;s timestamp to the log. If the log size is less than or equal to the limit, the request is accepted; otherwise it is rejected. In this variant, timestamps of rejected requests stay in the log as well, so a client that keeps hammering the endpoint doesn&amp;rsquo;t get back in until it actually backs off.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; limit = 5 / 60s, now = 100s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; log at the previous request (t = 99)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ◀──────────── window (39, 99] ────────────▶
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────┬──────┬──────┬──────┬──────┬──────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 40 │ 42 │ 58 │ 71 │ 90 │ 99 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──┬───┴──────┴──────┴──────┴──────┴──────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ ≤ now-60 = 40 → expired, removed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; remove 40, append the new request&amp;#39;s timestamp (100)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────┬──────┬──────┬──────┬──────┬──────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 42 │ 58 │ 71 │ 90 │ 99 │ 100 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┴──────┴──────┴──────┴──────┴──────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ◀─────────── window (40, 100] ────────────▶
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; count = 6 &amp;gt; 5 → new request rejected
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="characteristics-3"&gt;Characteristics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Accurate&lt;/li&gt;
&lt;li&gt;Stores a timestamp per request → memory grows with the number of incoming requests in the window (including rejected ones)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pros-3"&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Precise limiting: in any window, the number of accepted requests never exceeds the limit&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="cons-3"&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Uses a lot of memory, since timestamps of rejected requests are kept too&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sliding-window-counter"&gt;Sliding Window Counter&lt;/h3&gt;
&lt;p&gt;A refinement of Fixed Window Counter that approximates Sliding Window Log with just two counters. With &lt;code&gt;window = 1 min&lt;/code&gt;, estimate the number of requests in the last minute as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;estimate = requests in the current window (excluding the incoming one)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + requests in the previous window × fraction of the previous window that overlaps the last 1 min
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The incoming request is accepted if &lt;code&gt;estimate &amp;lt; limit&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; limit = 5 / 60s, window size 60s, now = 01:20 (1/3 into the current window)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; previous (00:00~01:00) current (01:00~02:00)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌────────────────────────┬────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ prev = 6 │ curr = 2 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └────────────────────────┴────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ◀─ sliding window (60s) ─▶
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──── overlap ───┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 00:20 01:00 01:20
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; overlap = 1 - 1/3 = 2/3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; estimate = curr + prev × overlap
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; = 2 + 6 × 2/3 = 6 ≥ 5 → rejected
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="characteristics-4"&gt;Characteristics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Stores only two counters (O(1) memory)&lt;/li&gt;
&lt;li&gt;An approximation that assumes requests in the previous window were evenly distributed&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pros-4"&gt;Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Smooths out the boundary problem of Fixed Window Counter by weighting in the previous window&lt;/li&gt;
&lt;li&gt;Memory efficient&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="cons-4"&gt;Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s an approximation, not exact: it may reject legitimate requests, or allow requests beyond the limit.
&lt;ul&gt;
&lt;li&gt;In practice, though, the error is small. Cloudflare measured it over 400 million requests and found that only 0.003% were wrongly allowed or rate limited. (&lt;a href="https://blog.cloudflare.com/counting-things-a-lot-of-different-things/"&gt;Counting things, a lot of different things…&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bursts around the boundary are only damped, not fully prevented&lt;/li&gt;
&lt;li&gt;Opaque to clients: the exact remaining quota and the value to send in the &lt;code&gt;Retry-After&lt;/code&gt; header are hard to compute&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="handling-requests-over-the-limit"&gt;Handling Requests Over the Limit&lt;/h2&gt;
&lt;p&gt;To tell a client it has been rate limited, respond with the &lt;code&gt;429 Too Many Requests&lt;/code&gt; status code, and use the &lt;code&gt;Retry-After&lt;/code&gt; header to say when it can try again (&lt;a href="https://www.rfc-editor.org/rfc/rfc6585#section-4"&gt;RFC 6585&lt;/a&gt;). Headers like &lt;code&gt;X-RateLimit-Limit&lt;/code&gt;, &lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;, and &lt;code&gt;X-RateLimit-Reset&lt;/code&gt; are also widely used (GitHub and Twitter, among others), but they are a convention rather than a standard; the IETF is standardizing &lt;code&gt;RateLimit&lt;/code&gt; and &lt;code&gt;RateLimit-Policy&lt;/code&gt; headers in &lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/"&gt;draft-ietf-httpapi-ratelimit-headers&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>