<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ridwan Olanrewaju Azeez</title>
    <description>The latest articles on DEV Community by Ridwan Olanrewaju Azeez (@jpegcreate).</description>
    <link>https://dev.to/jpegcreate</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3812977%2F06202522-f800-408e-a267-d1fd63e78510.jpeg</url>
      <title>DEV Community: Ridwan Olanrewaju Azeez</title>
      <link>https://dev.to/jpegcreate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jpegcreate"/>
    <language>en</language>
    <item>
      <title>Building a Distributed Rate Limiter for FastAPI with Redis (Sliding Window Algorithm)</title>
      <dc:creator>Ridwan Olanrewaju Azeez</dc:creator>
      <pubDate>Sun, 08 Mar 2026 16:12:38 +0000</pubDate>
      <link>https://dev.to/jpegcreate/building-a-distributed-rate-limiter-for-fastapi-with-redis-sliding-window-algorithm-5h10</link>
      <guid>https://dev.to/jpegcreate/building-a-distributed-rate-limiter-for-fastapi-with-redis-sliding-window-algorithm-5h10</guid>
      <description>&lt;h2&gt;
  
  
  Building a Distributed Rate Limiter for FastAPI with Redis
&lt;/h2&gt;

&lt;p&gt;Every API eventually runs into the same problem.&lt;/p&gt;

&lt;p&gt;A bot, scraper, or even a buggy client suddenly starts sending thousands of requests per second. When that happens, your server slows down, your database struggles, and real users start seeing errors.&lt;/p&gt;

&lt;p&gt;This is exactly the kind of situation &lt;strong&gt;rate limiting&lt;/strong&gt; is meant to prevent.&lt;/p&gt;

&lt;p&gt;Recently I built &lt;strong&gt;RateGuard&lt;/strong&gt;, a small Python library that adds distributed rate limiting to FastAPI using Redis. In this post I want to walk through how it works and the design decisions behind it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built RateGuard
&lt;/h2&gt;

&lt;p&gt;While working with FastAPI, I looked at a few rate limiting libraries. Most of them had at least one issue.&lt;/p&gt;

&lt;p&gt;Some only support &lt;strong&gt;in-memory limits&lt;/strong&gt;, which means they break once your API runs on multiple servers.&lt;/p&gt;

&lt;p&gt;Others work in distributed setups but require more infrastructure than I wanted.&lt;/p&gt;

&lt;p&gt;So I decided to build something simple with a few goals in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easy to plug into FastAPI&lt;/li&gt;
&lt;li&gt;works across multiple servers&lt;/li&gt;
&lt;li&gt;accurate under heavy traffic&lt;/li&gt;
&lt;li&gt;simple enough to understand and maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That became &lt;strong&gt;RateGuard&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Rate Limiting?
&lt;/h2&gt;

&lt;p&gt;Rate limiting controls how many requests a user can send to an API within a certain time period.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a user can send &lt;strong&gt;10 requests per minute&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;after the limit is reached they receive a &lt;code&gt;429 Too Many Requests&lt;/code&gt; response&lt;/li&gt;
&lt;li&gt;after the time window passes the limit resets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple analogy is a coffee shop rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One free coffee per customer per hour.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The barista keeps track of who got a coffee and when. If you come back too soon, you have to wait.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Just Use a Counter?
&lt;/h2&gt;

&lt;p&gt;A very common approach is to use a simple counter that resets every minute.&lt;/p&gt;

&lt;p&gt;The problem is that this method can be abused.&lt;/p&gt;

&lt;p&gt;Imagine your limit is &lt;strong&gt;10 requests per minute&lt;/strong&gt; and the counter resets at exactly &lt;strong&gt;12:00:00&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A user could do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send 10 requests at 11:59:55&lt;/li&gt;
&lt;li&gt;the counter resets at 12:00:00&lt;/li&gt;
&lt;li&gt;send another 10 requests at 12:00:05&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That ends up being &lt;strong&gt;20 requests in about 10 seconds&lt;/strong&gt;, even though the limit is supposed to be 10 per minute.&lt;/p&gt;

&lt;p&gt;This issue is called the &lt;strong&gt;fixed window problem&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Sliding Window Approach
&lt;/h2&gt;

&lt;p&gt;To avoid this problem, RateGuard uses the &lt;strong&gt;sliding window algorithm&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of resetting at fixed times, it always looks back a certain number of seconds from the current request.&lt;/p&gt;

&lt;p&gt;The logic looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a request arrives at time &lt;code&gt;T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;check all requests between &lt;code&gt;T - window&lt;/code&gt; and &lt;code&gt;T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;remove anything older than the window&lt;/li&gt;
&lt;li&gt;count the remaining requests&lt;/li&gt;
&lt;li&gt;if the count is below the limit, allow the request&lt;/li&gt;
&lt;li&gt;otherwise return a &lt;code&gt;429&lt;/code&gt; response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Going back to the coffee shop example, instead of resetting every hour on the clock, the barista asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did this person get a coffee in the last 60 minutes?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The time window moves forward with every request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Architecture
&lt;/h2&gt;

&lt;p&gt;RateGuard sits between incoming requests and your FastAPI application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client Request
      |
      v
FastAPI Server
      |
      v
RateGuard Middleware
      |
      v
Redis Sorted Set
      |
      v
Allow or Block Request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis stores request timestamps so every server in the system can see them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Redis?
&lt;/h2&gt;

&lt;p&gt;Redis is a good fit for rate limiting for two main reasons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed
&lt;/h3&gt;

&lt;p&gt;Rate limiting runs on &lt;strong&gt;every request&lt;/strong&gt;, so it has to be fast. Redis is an in-memory data store and can handle a huge number of operations per second.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared state
&lt;/h3&gt;

&lt;p&gt;If your API runs on several servers, each one needs to know how many requests have already been made. Redis works as a shared store that all servers can read from and write to.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using Redis Sorted Sets
&lt;/h2&gt;

&lt;p&gt;RateGuard stores request data inside a &lt;strong&gt;Redis Sorted Set&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A sorted set stores values with a score. The score determines the order.&lt;/p&gt;

&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;score&lt;/strong&gt; is the request timestamp&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;value&lt;/strong&gt; is a unique request ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Key: ratelimit:192.168.1.1

1709856060000 -&amp;gt; req_abc123
1709856080000 -&amp;gt; req_def456
1709856100000 -&amp;gt; req_xyz789
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each request, RateGuard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;removes entries older than the time window&lt;/li&gt;
&lt;li&gt;counts the remaining entries&lt;/li&gt;
&lt;li&gt;decides whether to allow or block the request&lt;/li&gt;
&lt;li&gt;records the new request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach works well even when multiple servers are handling traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing RateGuard
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;rate-guardian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also need a Redis instance. I used Upstash Redis, which has a generous free tier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rateguard&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RateGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RateLimitMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate_limit&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RateGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;redis_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPSTASH_REDIS_REST_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;redis_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPSTASH_REDIS_REST_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;RateLimitMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API is protected by RateGuard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding the middleware, every endpoint is automatically protected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Per Route Limits
&lt;/h2&gt;

&lt;p&gt;Sometimes you want stricter limits for certain endpoints.&lt;/p&gt;

&lt;p&gt;For example, a search endpoint that queries a database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@rate_limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;/search&lt;/code&gt; only allows &lt;strong&gt;5 requests per minute&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Response Headers
&lt;/h2&gt;

&lt;p&gt;RateGuard includes useful headers in responses.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;X-RateLimit-Limit&lt;/td&gt;
&lt;td&gt;Maximum allowed requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X-RateLimit-Remaining&lt;/td&gt;
&lt;td&gt;Requests left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X-RateLimit-Reset&lt;/td&gt;
&lt;td&gt;Seconds until reset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry-After&lt;/td&gt;
&lt;td&gt;Only present on 429 responses&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These help clients know when to slow down.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens If Redis Fails?
&lt;/h2&gt;

&lt;p&gt;One design decision I made was to &lt;strong&gt;fail open&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If Redis is temporarily unavailable, requests are allowed instead of blocked.&lt;/p&gt;

&lt;p&gt;For most APIs, blocking all traffic because Redis is down would be worse than briefly running without rate limiting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Logic Example
&lt;/h2&gt;

&lt;p&gt;Here is a simplified version of the main logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_allowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;oldest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zremrangebyscore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zcard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zadd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a pipeline groups the Redis operations together and avoids race conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;A few improvements I plan to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;support for standard Redis deployments&lt;/li&gt;
&lt;li&gt;rate limiting by user ID&lt;/li&gt;
&lt;li&gt;an optional token bucket algorithm&lt;/li&gt;
&lt;li&gt;better metrics and monitoring&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;rate-guardian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/Jpeg-create/rate-guard" rel="noopener noreferrer"&gt;https://github.com/Jpeg-create/rate-guard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyPI: &lt;a href="https://pypi.org/project/rate-guardian/" rel="noopener noreferrer"&gt;https://pypi.org/project/rate-guardian/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find it useful, a ⭐ on GitHub means a lot.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>redis</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
