<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: rate-limiting</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/rate-limiting.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-05-14T04:10:23+00:00</updated><author><name>Simon Willison</name></author><entry><title>datasette-ip-rate-limit 0.1a0</title><link href="https://simonwillison.net/2026/May/14/datasette-ip-rate-limit/#atom-tag" rel="alternate"/><published>2026-05-14T04:10:23+00:00</published><updated>2026-05-14T04:10:23+00:00</updated><id>https://simonwillison.net/2026/May/14/datasette-ip-rate-limit/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-ip-rate-limit/releases/tag/0.1a0"&gt;datasette-ip-rate-limit 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The &lt;a href="https://datasette.io/"&gt;datasette.io&lt;/a&gt; site was being hammered by poorly-behaved crawlers, so I had Codex (GPT-5.5 xhigh) build a configurable rate limiting plugin to block IPs that were hammering specific areas of the site too quickly.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://github.com/simonw/datasette.io/blob/b6022bf9987661b94a26d3143028193a6cabfdcf/datasette.yml#L103-L116"&gt;the production configuration&lt;/a&gt; I'm using on that site for the new plugin:&lt;/p&gt;
&lt;pre&gt;  &lt;span class="pl-ent"&gt;datasette-ip-rate-limit&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;header&lt;/span&gt;: &lt;span class="pl-s"&gt;Fly-Client-IP&lt;/span&gt;
    &lt;span class="pl-ent"&gt;max_keys&lt;/span&gt;: &lt;span class="pl-c1"&gt;10000&lt;/span&gt;
    &lt;span class="pl-ent"&gt;exempt_paths&lt;/span&gt;:
    - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/static/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/-/turnstile*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    &lt;span class="pl-ent"&gt;rules&lt;/span&gt;:
    - &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;demo-databases&lt;/span&gt;
      &lt;span class="pl-ent"&gt;paths&lt;/span&gt;:
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/global-power-plants/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/legislators/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
      &lt;span class="pl-ent"&gt;window_seconds&lt;/span&gt;: &lt;span class="pl-c1"&gt;60&lt;/span&gt;
      &lt;span class="pl-ent"&gt;max_requests&lt;/span&gt;: &lt;span class="pl-c1"&gt;60&lt;/span&gt;
      &lt;span class="pl-ent"&gt;block_seconds&lt;/span&gt;: &lt;span class="pl-c1"&gt;20&lt;/span&gt;&lt;/pre&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/codex"&gt;codex&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="rate-limiting"/><category term="datasette"/><category term="codex"/></entry><entry><title>TIL: Rate limiting by IP using Cloudflare's rate limiting rules</title><link href="https://simonwillison.net/2025/Jul/3/rate-limiting-by-ip/#atom-tag" rel="alternate"/><published>2025-07-03T21:16:51+00:00</published><updated>2025-07-03T21:16:51+00:00</updated><id>https://simonwillison.net/2025/Jul/3/rate-limiting-by-ip/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/cloudflare/rate-limiting"&gt;TIL: Rate limiting by IP using Cloudflare&amp;#x27;s rate limiting rules&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My blog started timing out on some requests a few days ago, and it turned out there were misbehaving crawlers that were spidering my &lt;code&gt;/search/&lt;/code&gt; page even though it's restricted by &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I run this site behind Cloudflare and it turns out Cloudflare's WAF (Web Application Firewall) has a rate limiting tool that I could use to restrict requests to &lt;code&gt;/search/*&lt;/code&gt; by a specific IP to a maximum of 5 every 10 seconds.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cloudflare"&gt;cloudflare&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/til"&gt;til&lt;/a&gt;&lt;/p&gt;



</summary><category term="rate-limiting"/><category term="security"/><category term="cloudflare"/><category term="til"/></entry><entry><title>Reservoir Sampling</title><link href="https://simonwillison.net/2025/May/8/reservoir-sampling/#atom-tag" rel="alternate"/><published>2025-05-08T21:00:22+00:00</published><updated>2025-05-08T21:00:22+00:00</updated><id>https://simonwillison.net/2025/May/8/reservoir-sampling/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://samwho.dev/reservoir-sampling/"&gt;Reservoir Sampling&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Yet another outstanding interactive essay by Sam Rose (&lt;a href="https://simonwillison.net/tags/sam-rose/"&gt;previously&lt;/a&gt;), this time explaining how reservoir sampling can be used to select a "fair" random sample when you don't know how many options there are and don't want to accumulate them before making a selection.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reservoir sampling is one of my favourite algorithms, and I've been wanting to write about it for years now. It allows you to solve a problem that at first seems impossible, in a way that is both elegant and efficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I appreciate that Sam starts the article with "No math notation, I promise." Lots of delightful widgets to interact with here, all of which help build an intuitive understanding of the underlying algorithm.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo. As a slider moves from left to right the probability of cards drawn from a deck is simulated. Text at the bottom reads Anything older than 15 cards ago is has a less than 0.01% chance of being held when I stop." src="https://static.simonwillison.net/static/2025/sam-rose-cards.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Sam shows how this algorithm can be applied to the real-world problem of sampling log files when incoming logs threaten to overwhelm a log aggregator.&lt;/p&gt;
&lt;p&gt;The dog illustration is &lt;a href="https://samwho.dev/dogs/"&gt;commissioned art&lt;/a&gt; and the MIT-licensed code is &lt;a href="https://github.com/samwho/visualisations/tree/main/reservoir-sampling"&gt;available on GitHub&lt;/a&gt;.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=43928315"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/algorithms"&gt;algorithms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/logging"&gt;logging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/explorables"&gt;explorables&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sam-rose"&gt;sam-rose&lt;/a&gt;&lt;/p&gt;



</summary><category term="algorithms"/><category term="logging"/><category term="rate-limiting"/><category term="explorables"/><category term="sam-rose"/></entry><entry><title>DeepSeek API Docs: Rate Limit</title><link href="https://simonwillison.net/2025/Jan/18/deepseek-api-docs-rate-limit/#atom-tag" rel="alternate"/><published>2025-01-18T18:24:38+00:00</published><updated>2025-01-18T18:24:38+00:00</updated><id>https://simonwillison.net/2025/Jan/18/deepseek-api-docs-rate-limit/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/rate_limit"&gt;DeepSeek API Docs: Rate Limit&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is surprising: DeepSeek offer the only hosted LLM API I've seen that doesn't implement rate limits:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DeepSeek API does NOT constrain user's rate limit. We will try out best to serve every request.&lt;/p&gt;
&lt;p&gt;However, please note that when our servers are under high traffic pressure, your requests may take some time to receive a response from the server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Want to run a prompt against 10,000 items? With DeepSeek you can theoretically fire up 100s of parallel requests and crunch through that data in almost no time at all.&lt;/p&gt;
&lt;p&gt;As more companies start building systems that rely on LLM prompts for large scale data extraction and manipulation I expect high rate limits will become a key competitive differentiator between the different platforms.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deepseek"&gt;deepseek&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-in-china"&gt;ai-in-china&lt;/a&gt;&lt;/p&gt;



</summary><category term="rate-limiting"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="deepseek"/><category term="ai-in-china"/></entry><entry><title>aiolimiter</title><link href="https://simonwillison.net/2024/Feb/20/aiolimiter/#atom-tag" rel="alternate"/><published>2024-02-20T01:15:27+00:00</published><updated>2024-02-20T01:15:27+00:00</updated><id>https://simonwillison.net/2024/Feb/20/aiolimiter/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://aiolimiter.readthedocs.io/"&gt;aiolimiter&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I found myself wanting an asyncio rate limiter for Python today—so I could send POSTs to an API endpoint no more than once every 10 seconds. This library worked out really well—it has a very neat design and lets you set up rate limits for things like “no more than 50 items every 10 seconds”, implemented using the leaky bucket algorithm.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;&lt;/p&gt;



</summary><category term="async"/><category term="python"/><category term="rate-limiting"/></entry><entry><title>Quoting Push notification two-factor auth considered harmful</title><link href="https://simonwillison.net/2022/Sep/17/push-notification-two-factor-auth-considered-harmful/#atom-tag" rel="alternate"/><published>2022-09-17T14:45:21+00:00</published><updated>2022-09-17T14:45:21+00:00</updated><id>https://simonwillison.net/2022/Sep/17/push-notification-two-factor-auth-considered-harmful/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://xeiaso.net/blog/push-2fa-considered-harmful"&gt;&lt;p&gt;However, six digits is a very small space to search through when you are a computer. The biggest problem is going to be getting lucky, it's quite literally a one-in-a-million shot. Turns out you can brute force a TOTP code in about 2 hours if you are careful and the remote service doesn't have throttling or rate limiting of authentication attempts.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://xeiaso.net/blog/push-2fa-considered-harmful"&gt;Push notification two-factor auth considered harmful&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;&lt;/p&gt;



</summary><category term="rate-limiting"/><category term="security"/></entry><entry><title>Scaling a High-traffic Rate Limiting Stack With Redis Cluster</title><link href="https://simonwillison.net/2018/Apr/26/redis-cluster/#atom-tag" rel="alternate"/><published>2018-04-26T18:34:25+00:00</published><updated>2018-04-26T18:34:25+00:00</updated><id>https://simonwillison.net/2018/Apr/26/redis-cluster/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://brandur.org/redis-cluster"&gt;Scaling a High-traffic Rate Limiting Stack With Redis Cluster&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Brandur Leach describes the simple, elegant and performant design of Redis Cluster, and talks about how Stripe used it to scaled their rate-limiting from one to ten nodes.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/brandur-leach"&gt;brandur-leach&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/stripe"&gt;stripe&lt;/a&gt;&lt;/p&gt;



</summary><category term="rate-limiting"/><category term="redis"/><category term="scaling"/><category term="brandur-leach"/><category term="stripe"/></entry><entry><title>How could GitHub improve the password security of its users?</title><link href="https://simonwillison.net/2013/Nov/20/how-could-github-improve/#atom-tag" rel="alternate"/><published>2013-11-20T17:50:00+00:00</published><updated>2013-11-20T17:50:00+00:00</updated><id>https://simonwillison.net/2013/Nov/20/how-could-github-improve/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-could-GitHub-improve-the-password-security-of-its-users/answer/Simon-Willison"&gt;How could GitHub improve the password security of its users?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By doing exactly what they're doing already: adding more sophisticated rate limiting, and preventing users from using common weak passwords.&lt;/p&gt;

&lt;p&gt;Their account security practices are already best-in-industry: they support two-factor authentication and their "Security History" interface at &lt;span&gt;&lt;a href="https://github.com/settings/security"&gt;https://github.com/settings/secu...&lt;/a&gt;&lt;/span&gt; is the best I've seen on any website.&lt;/p&gt;

&lt;p&gt;The way they store passwords (correctly, using bcrypt) had nothing to do with this particular security incident.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/passwords"&gt;passwords&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="github"/><category term="open-source"/><category term="passwords"/><category term="rate-limiting"/><category term="security"/><category term="quora"/></entry><entry><title>Does Twitter use a 3rd party software for rate limiting their APIs? If yes, who's the 3rd party?</title><link href="https://simonwillison.net/2010/Nov/21/does-twitter-use-a/#atom-tag" rel="alternate"/><published>2010-11-21T14:27:00+00:00</published><updated>2010-11-21T14:27:00+00:00</updated><id>https://simonwillison.net/2010/Nov/21/does-twitter-use-a/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Does-Twitter-use-a-3rd-party-software-for-rate-limiting-their-APIs-If-yes-whos-the-3rd-party/answer/Simon-Willison"&gt;Does Twitter use a 3rd party software for rate limiting their APIs? If yes, who&amp;#39;s the 3rd party?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wrote up a technique for doing simple rate limiting using memcached a while ago, which I later found out was somewhat similar to how the Twitter API does it.&lt;/p&gt;

&lt;span&gt;&lt;a href="http://simonwillison.net/2009/Jan/7/ratelimitcache/"&gt;http://simonwillison.net/2009/Ja...&lt;/a&gt;&lt;/span&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apis"&gt;apis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/twitter"&gt;twitter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="apis"/><category term="rate-limiting"/><category term="twitter"/><category term="quora"/></entry><entry><title>Rate limiting with memcached</title><link href="https://simonwillison.net/2009/Jan/7/ratelimitcache/#atom-tag" rel="alternate"/><published>2009-01-07T22:27:08+00:00</published><updated>2009-01-07T22:27:08+00:00</updated><id>https://simonwillison.net/2009/Jan/7/ratelimitcache/#atom-tag</id><summary type="html">
    &lt;p&gt;On Monday, several high profile "celebrity" Twitter accounts &lt;a href="http://www.techcrunch.com/2009/01/05/twitter-gets-hacked-badly/" title="Twitter Gets Hacked, Badly"&gt;started spouting nonsense&lt;/a&gt;, the victims of stolen passwords. Wired &lt;a href="http://blog.wired.com/27bstroke6/2009/01/professed-twitt.html" title="Weak Password Brings 'Happiness' to Twitter Hacker"&gt;has the full story&lt;/a&gt; - someone ran a dictionary attack against a Twitter staff member, discovered their password and used Twitter's admin tools to reset the passwords on the accounts they wanted to steal.&lt;/p&gt;

&lt;p&gt;The Twitter incident got me thinking about rate limiting again. I've been wanting a good general solution to this problem for quite a while, for API projects as well as security. Django Snippets has &lt;a href="http://www.djangosnippets.org/snippets/1083/" title="Decorator to limit request rates to individual views"&gt;an answer&lt;/a&gt;, but it works by storing access information in the database and requires you to run a periodic purge command to clean up the old records.&lt;/p&gt;

&lt;p&gt;I'm strongly averse to writing to the database for every hit. For most web applications reads scale easily, but writes don't. I also want to avoid filling my database with administrative gunk (I dislike database backed sessions for the same reason). But rate limiting relies on storing state, so there has to be some kind of persistence.&lt;/p&gt;

&lt;h4&gt;Using memcached counters&lt;/h4&gt;

&lt;p&gt;I think I've found a solution, thanks to memcached and in particular the &lt;samp&gt;incr&lt;/samp&gt; command. &lt;samp&gt;incr&lt;/samp&gt; lets you atomically increment an already existing counter, simply by specifying its key. &lt;samp&gt;add&lt;/samp&gt; can be used to create that counter - it will fail silently if the provided key already exists.&lt;/p&gt;

&lt;p&gt;Let's say we want to limit a user to 10 hits every minute. A naive implementation would be to create a memcached counter for hits from that user's IP address in a specific minute. The counter key might look like this:&lt;/p&gt;

&lt;pre&gt;&lt;samp&gt;ratelimit_72.26.203.98_2009-01-07-21:45&lt;/samp&gt;&lt;/pre&gt;

&lt;p&gt;Increment that counter for every hit, and if it exceeds 10 block the request.&lt;/p&gt;

&lt;p&gt;What if the user makes ten requests all in the last second of the minute, then another ten a second later? The rate limiter will let them off. For many cases this is probably acceptable, but we can improve things with a slightly more complex strategy. Let's say we want to allow up to 30 requests every five minutes. Instead of maintaining one counter, we can maintain five - one for each of the past five minutes (older counters than that are allowed to expire). After a few minutes we might end up with counters that look like this:&lt;/p&gt;

&lt;pre&gt;&lt;samp&gt;ratelimit_72.26.203.98_2009-01-07-21:45 = 13
ratelimit_72.26.203.98_2009-01-07-21:46 = 7
ratelimit_72.26.203.98_2009-01-07-21:47 = 11&lt;/samp&gt;&lt;/pre&gt;

&lt;p&gt;Now, on every request we work out the keys for the past five minutes and use &lt;samp&gt;get_multi&lt;/samp&gt; to retrieve them. If the sum of those counters exceeds the maximum allowed for that time period, we block the request.&lt;/p&gt;

&lt;p&gt;Are there any obvious flaws to this approach? I'm pretty happy with it - it cleans up after itself (old counters quietly expire from the cache), it shouldn't use much resources (just five active cache keys per unique IP address at any one time) and if the cache is lost the only snag is that a few clients might go slightly over their rate limit. I don't &lt;em&gt;think&lt;/em&gt; it's possible for an attacker to force the counters to expire early.&lt;/p&gt;

&lt;h4&gt;An implementation for Django&lt;/h4&gt;

&lt;p&gt;I've put together an &lt;a href="http://github.com/simonw/ratelimitcache/tree/master/ratelimitcache.py"&gt;example implementation of this algorithm&lt;/a&gt; using Django, hosted on GitHub. The &lt;a href="http://github.com/simonw/ratelimitcache/tree/master/readme.txt"&gt;readme.txt&lt;/a&gt; file shows how it works - basic usage is via a simple decorator:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;from ratelimitcache import ratelimit

@ratelimit(minutes = 3, requests = 20)
def myview(request):
    # ...
    return HttpResponse('...')&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Python decorators are typically functions, but &lt;code&gt;ratelimit&lt;/code&gt; is actually a class. This means it can be customised by subclassing it, and the class provides a number of methods designed to be over-ridden. I've provided an example of this in the module itself - ratelimit_post, a decorator which only limits on POST requests and can optionally couple the rate limiting to an individual POST field. Here's the complete implementation:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;class ratelimit_post(ratelimit):
    "Rate limit POSTs - can be used to protect a login form"
    key_field = None # If provided, this POST var will affect the rate limit
    
    def should_ratelimit(self, request):
        return request.method == 'POST'
    
    def key_extra(self, request):
        # IP address and key_field (if it is set)
        extra = super(ratelimit_post, self).key_extra(request)
        if self.key_field:
            value = sha.new(request.POST.get(self.key_field, '')).hexdigest()
            extra += '-' + value
        return extra&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here's how you would use it to limit the number of times a specific IP address can attempt to log in as a particular user:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;@ratelimit_post(minutes = 3, requests = 10, key_field = 'username')
def login(request):
    # ...
    return HttpResponse('...')&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;should_ratelimit()&lt;/code&gt; method is called before any other rate limiting logic. The default implementation returns True, but here we only want to apply rate limits to POST requests. The &lt;code&gt;key_extra()&lt;/code&gt; method is used to compose the keys used for the counter - by default this just includes the request's IP address, but in &lt;code&gt;ratelimit_post&lt;/code&gt; we can optionally include the value of a POST field (for example the username). We could include things like the request path here to apply different rate limit counters to different URLs.&lt;/p&gt;

&lt;p&gt;Finally, the readme.txt includes &lt;code&gt;ratelimit_with_logging&lt;/code&gt;, an example that over-rides the &lt;code&gt;disallowed()&lt;/code&gt; view returned when a rate limiting condition fails and writes an audit note to a database (less overhead than writing for every request).&lt;/p&gt;

&lt;p&gt;I've been a fan of customisation via subclassing ever since I got to know the new Django admin system, and I've been using it in a bunch of projects. It's a great way to create reusable pieces of code.&lt;/p&gt;

    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/counters"&gt;counters&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ratelimitcache"&gt;ratelimitcache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/twitter"&gt;twitter&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="counters"/><category term="django"/><category term="github"/><category term="memcached"/><category term="projects"/><category term="python"/><category term="ratelimitcache"/><category term="rate-limiting"/><category term="security"/><category term="twitter"/></entry><entry><title>Decorator to limit request rates to individual views</title><link href="https://simonwillison.net/2008/Sep/24/django/#atom-tag" rel="alternate"/><published>2008-09-24T13:13:29+00:00</published><updated>2008-09-24T13:13:29+00:00</updated><id>https://simonwillison.net/2008/Sep/24/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1083/"&gt;Decorator to limit request rates to individual views&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat piece of code for public facing web APIs written in Django. Update: some smart criticisms in the comments.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apis"&gt;apis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/decorators"&gt;decorators&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;&lt;/p&gt;



</summary><category term="apis"/><category term="decorators"/><category term="django"/><category term="python"/><category term="rate-limiting"/></entry></feed>