Skip to content


Subversion checkout URL

You can clone with
Download ZIP
A memcached backed rate limiting decorator for Django.
Branch: master

expire_after is now a method, not a property - which means it always …

…takes the current value for self.minutes in to account. Thanks Matthew Somerville.
latest commit 591db0eb3a
Simon Willison authored


ratelimitcache for Django
By Simon Willison -

A rate limiter that uses Django's cache framework, with no requirement for a 
persistent data store.

More information:

    Place the on your Python path.
    Configure your CACHE_BACKEND setting. For best results, use the memcached 
    backend - the other backends do not provide an atomic counter increment 
    and so may suffer from less effective limiting due to race conditions.
    Cache documentation:

    cd demo/
    ./ runserver 8008
    Now browse to:

Basic usage (max 20 requests every 3 minutes):

    from ratelimitcache import ratelimit
    @ratelimit(minutes = 3, requests = 20)
    def myview(request):
        # ...
        return HttpResponse('...')

Protecting a login form, i.e rate limit on IP address and attempted username:
    from ratelimitcache import ratelimit_post
    @ratelimit_post(minutes = 3, requests = 10, key_field = 'username')
    def login(request):
        # ...
        return HttpResponse('...')

You can also use it directly in Here's how you would use it with 
the login() view function provided by Django:
    from ratelimitcache import ratelimit_post
    from django.contrib.auth.views import login
    urlpatterns = patterns('',
        (r'^login/$', ratelimit_post(
            minutes = 3, requests = 10, key_field = 'username'

Custom behaviour, e.g. logging when the rate limit condition fails:
    from ratelimitcache import ratelimit
    from my_logging_app.models import Log
    import datetime, pprint
    class ratelimit_with_logging(ratelimit):
        def disallowed(self, request):
                ip_address = request.META.get('REMOTE_ADDR'),
                path = request.path,
                counters = pprint.pformat(
                created =
            return HttpResponseForbidden('Rate limit exceeded')
    @ratelimit_with_logging(minutes = 3, requests = 20)
    def myview(request):
        # ...
        return HttpResponse('...')
Something went wrong with that request. Please try again.