Rate Limiting extension for Flask

Related tags

Flaskflask-limiter
Overview

Flask-Limiter

docs ci codecov pypi license

Flask-Limiter provides rate limiting features to flask routes. It has support for a configurable backend for storage with current implementations for in-memory, redis and memcache.

Quickstart

Add the rate limiter to your flask app. The following example uses the default in memory implementation for storage.

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["2 per minute", "1 per second"],
)

@app.route("/slow")
@limiter.limit("1 per day")
def slow():
    return "24"

@app.route("/fast")
def fast():
    return "42"

@app.route("/ping")
@limiter.exempt
def ping():
    return 'PONG'

app.run()

Test it out. The fast endpoint respects the default rate limit while the slow endpoint uses the decorated one. ping has no rate limit associated with it.

$ curl localhost:5000/fast
42
$ curl localhost:5000/fast
42
$ curl localhost:5000/fast
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>429 Too Many Requests</title>
<h1>Too Many Requests</h1>
<p>2 per 1 minute</p>
$ curl localhost:5000/slow
24
$ curl localhost:5000/slow
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>429 Too Many Requests</title>
<h1>Too Many Requests</h1>
<p>1 per 1 day</p>
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG
$ curl localhost:5000/ping
PONG

Read the docs

Comments
  • Issue at Litespeed server and Python 3.9.12

    Issue at Litespeed server and Python 3.9.12

    Hi,

    I am facing an intermittent issue with flask-limiter running on Python 3.9.12 on a Litespeed server.

    Sometimes the web page loads, but most of the time, does not load. You can see the attached images... Loaded in the first access (counter = 1), but in the second time, after a page reload (at browser), it keep trying to load, and after some time returns "Request Timeout" error.

    Here is the full python code:

    from flask import Flask, request
    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    
    app = Flask(__name__)
    limiter = Limiter(app, key_func=get_remote_address, default_limits=['300/day'], enabled=True)
    
    
    counter = 0
    @app.route('/')
    @limiter.limit('200 per day')
    @limiter.limit('50 per hour')
    @limiter.limit('10 per minute')
    def hello_world():
        global counter
        counter = counter + 1
        return f'Hello World! Visit number: {counter}'
    
    
    if __name__ == '__main__':
        app.run()
    
    

    If I set enabled=False then the problem disappears: limiter = Limiter(app, key_func=get_remote_address, default_limits=['300/day'], enabled=False)

    My best guess is some incompatibility with Litespeed server and/or its native WSGI. Any idea on how to fix?

    flask-liimiter-problem flask-liimiter-problem-2 .

    opened by sukhoi47 20
  • Limit decorator for non standard routing (Flask-Restful)

    Limit decorator for non standard routing (Flask-Restful)

    Current situation: When the decorator is loaded, a route is added to route_limits or dynamic_route_limits. The name of the route is defined like this:

    name = "%s.%s" % (fn.__module__, fn.__name__)
    

    Then for every 'before request', __check_request_limit is called. This function tries to find the correct name for the end point and see if there is a limit defined for it in either dictionaries:

    endpoint = request.endpoint or ""
            view_func = current_app.view_functions.get(endpoint, None)
            name = ("%s.%s" % (
                    view_func.__module__, view_func.__name__
                ) if view_func else ""
    

    This works well for standard routing. But now I want to use Flask-Limiter for Flask-Restful routes. And this creates a problem because the endpoint name when checking the limit will be different than the name that is determined when the decorator is defined.

    For example, module myapp.api:

    class HelloWorld(restful.Resource):
        @limiter.limit("1/minute")
        def get(self):
            return {'hello': 'world'}
    
    api.add_resource(HelloWorld, '/')
    

    would create something like myapp.api.get as name for the limiter route, but is actually defined as myapp.api.helloworld by Flask-Restful. Of course this is not specific to Flask-Restful, and can occur any time the limiter is used on a function that is not also the actual endpoint, I think.

    Any thoughts on how to fix this in a nice way? Maybe the checking of the rate limiting could be better done directly in the decorator instead of 1 central before_request?

    enhancement question 
    opened by marijns 20
  • Share limits between several routes

    Share limits between several routes

    It looks like the current implementation does not allow sharing a single custom rate limit among several routes (global limits excluded).

    For instance, take the example where you have a /slow route limited to 1 request per day. What if I have a second route which I would like to share the limit of 1 request per day along the other /slow route? Right now the code will limit each route to 1 req per day, while it would be useful to allow 1 req per day for all the slow routes combined.

    enhancement 
    opened by g-p-g 17
  • Retry-After header reporting some optimistic, and occasionally strange values

    Retry-After header reporting some optimistic, and occasionally strange values

    Hi,

    I am having a slight issue with the Retry-After headers on my current project. If the user triggers too many requests to my rate limited endpoint, I am using the Retry-After header to wait for the right amount of time before letting their request through.

    However, it almost seems that the header is a little "optimistic" timewise, because if I retry after that exact number of seconds, it triggers the rate limit again. However, on this request, the rate limiter actually says Retry-After zero seconds. It says this several times in quick succession, before then saying Retry-After -1484742195 seconds (exact value varies, but it's always large and negative).

    I don't know whether I've misunderstood something, or if I'm doing something else daft, but I wondered how this value was calculated, and how reliable it was? If I arbitrarily add a few seconds to the Retry-After time it works fine, but I am reluctant to add a fixed value such as 5 seconds in case there are times when 5 seconds aren't enough. Adding 1 second doesn't seem to be sufficient, but adding 2 seconds seems to do the trick.

    Once that Retry-After is up, what actually happens internally? Is it simply that I am observing the rate limit resetting itself and taking a non-zero amount of time to do so?

    Thanks!

    opened by andymantell 15
  • Is it possible to abort with a custom exception (no 429 code)?

    Is it possible to abort with a custom exception (no 429 code)?

    The doc says : "The default configuration results in an abort(429) being called [...]".

    Is it possible to customize this behaviour, for example:

    • aborting with an other HTTP code
    • raising a custom exception
    • only logging the remote ip address
    opened by Horace89 13
  • Bundle decorator to run the check

    Bundle decorator to run the check

    I have disabled auto_check because I want to trigger rate-limit check at a specific point during request processing.

    My routes are something like this:

    @app.route(...)
    @auth.auth(...)
    ...
    def route():
        ...
    

    And I want limiter to run after auth check. So I added something like:

    class ExtLimiter(Limiter):
    
        def rate_limit(self):
            def wrapper(f):
                @functools.wraps(f)
                def wrapped(*args, **kwargs):
                    self.check()
                    return f(*args, **kwargs)
                return wrapped
            return wrapper
    

    And now I:

    @app.route(...)
    @auth.auth(...)
    @limiter.rate_limit()
    ...
    def route():
        ...
    

    I think this simple decorator could be bundled in Flask-Limiter?

    (I was kinda surprised when @limiter.limit() only registered the route and did not run the check() at all. So, added this decorator.)

    enhancement 
    opened by tuukkamustonen 13
  • Requirements missing

    Requirements missing

    I installed the package of version 0.8.1 using pip install and the package was unable to run since the limits package was missing. I just added manually the limits package to my requirements.

    Can you fix it please...

    opened by AlmogCohen 13
  • Requests that have variable cost?

    Requests that have variable cost?

    Hey guys, I'm curious whether flask-limiter could help when requests have variable cost. For instance, can I count a single request against the limit with some multiplier based on the content of the request?

    If not, do you all have any thoughts on how to help with this type or metering?

    enhancement 
    opened by milescrawford 12
  • unable to disable limiter during testing

    unable to disable limiter during testing

    How can we disable the rate limiter during our unit testing? I cannot get it to work. Setting RATELIMIT_ENABLED = False doesn't seem to work. I'd like to globally turn off the entire limiter since I use the limiter.limit() in a lot of different locations, but don't want to add testing conditions everywhere. Here is my app

    def create_app(object_config=ProdConfig):
        app = Flask(__name__)
        app.config.from_object(object_config)
        ...
        limiter.init_app(app)
        for handler in app.logger.handlers:
            limiter.logger.addHandler(handler)
    
        return app    
    

    and here is my testing app inside my pytest conftest.py

    from myapp import create_app
    import pytest
    
    @pytest.fixture(scope='session')
    def app():
        app = create_app(debug=True, local=True, object_config=TestConfig)
        return app
    

    I load it with a Test Config which has RATELIMIT_ENABLED set to False but it does not work

    class TestConfig(Config):
        TESTING = True
        DEBUG = True
        SQLALCHEMY_DATABASE_URI = 'sqlite://'
        BCRYPT_LOG_ROUNDS = 1  # For faster tests
        WTF_CSRF_ENABLED = False  # Allows form testing
        PRESERVE_CONTEXT_ON_EXCEPTION = False
        USE_PROFILER = False  # Turn off the Flask Profiler extension
        RATELIMIT_ENABLED = False  # Turn off the Flask Rate Limiter
    
    
    
    question 
    opened by havok2063 12
  • Possible to fall back to in-memory storage?

    Possible to fall back to in-memory storage?

    Is it possible currently to have it fall back to an in-memory storage scheme if the redis/memcached server was down? I didn't see anything in the code, so I assume the answer is "no". But just wanted to ask.

    I would assume this would be opt-in, not everyone would want this behavior. But I see a huge benefit to having the rate limiting just flip back to in-memory if the remote server was down.

    If the flask app was under high load, the request load could take down a memcache/redis server. So having it flip to in-memory means an endpoint could stay up and then the in-memory rate limit would get exceeded on each server and the offending client would get blocked (just would take longer as they'd have to hit limits on each individual server). While other clients would still get access.

    enhancement 
    opened by jonathanq 12
  • Q: Can we use multiple Limiters on a single app?

    Q: Can we use multiple Limiters on a single app?

    Hi alisaifee,

    I am trying to apply two sets of default limits on all routes, by using this code:

    limiter = Limiter(app,
        key_func=get_remote_address,
        default_limits=["30 / minute", "500 / hour", "2000 / day"],
        headers_enabled=True,
        storage_uri=LIMITER_URI,
        in_memory_fallback_enabled=True
    )
    global_limiter = Limiter(
        app,
        key_func=lambda:"global_bucket",
        default_limits=["100 / minute"],
        headers_enabled=True,
        storage_uri=LIMITER_URI,
        in_memory_fallback_enabled=True
    )
    

    This behaves differently than using global_limiter = limiter.shared_limit("100/minute", scope=lambda:"global_bucket") because this way, I wouldn't have to mention @global_limiterbefore all routes.

    However it appears global_limiter is not applied.

    Besides, I could not find any example of people using multiple Limiters on any app. Would you have an idea of how to accomplish this?

    enhancement 
    opened by Lalbatros 11
  • Cannot integrate with Flask-Application builder as viewfunc(endpoint) !=

    Cannot integrate with Flask-Application builder as viewfunc(endpoint) != "{obj.__module__}.{obj.__name__}"

    I am trying to add support for rate limiting in Flask-Application-Builder (FAB). This means that the limiter is instantiated as part of appbuilder initalizing and will be maintained in app.appbuilder.sm.limiter (sm is the security manager of FAB).

    FAB has several ways to provide logins. I tried decorating the login views as follows

    class AuthDBView(AuthView):
        login_template = "appbuilder/general/security/login_db.html"
    
        @expose("/login/", methods=["GET", "POST"])
        def login(self):
            @self.appbuilder.sm.limiter.limit(limit_value="2 per 5 seconds")
            def _login(self):
                if g.user is not None and g.user.is_authenticated:
                    return redirect(self.appbuilder.get_url_for_index)
                form = LoginForm_db()
                if form.validate_on_submit():
                    user = self.appbuilder.sm.auth_user_db(
                        form.username.data, form.password.data
                    )
                    if not user:
                        flash(as_unicode(self.invalid_login_message), "warning")
                        return redirect(self.appbuilder.get_url_for_login)
                    login_user(user, remember=False)
                    next_url = request.args.get("next", "")
                    return redirect(get_safe_redirect(next_url))
                return self.render_template(
                    self.login_template, title=self.title, form=form, appbuilder=self.appbuilder
                )
    
            return _login(self)
    

    This doesn't work. This is due to:

    name = f"{obj.__module__}.{obj.__name__}" evaluates to name being AuthDBView.login and in view_func = app.view_functions.get(endpoint, None) evaluates to flask_appbuilder.security.views.login (after __module__.__name__). This prevents lookups from working.

    If I set name = endpoint in route_limits and do marked_for_limiting = name in self._marked_for_limiting or endpoint in self._marked_for_limiting things start working.

    I can provide a PR for this, but I am not sure of this is the right approach :-)

    opened by bolkedebruin 7
Releases(3.1.0)
Lightweight library for providing filtering mechanism for your APIs using SQLAlchemy

sqlalchemy-filters-plus is a light-weight extendable library for filtering queries with sqlalchemy. Install pip install sqlalchemy-fitlers-plus Usage

Karami El Mehdi 38 Oct 05, 2022
Search users in Github. Created with Flask, PipEnv, Heroku and free time.

Search in Github Here search for users in Github and other stuff! This app is working with, Data Github API BackEnd Flask Language Python Package mana

AmirHossein Mohammadi 12 Jan 16, 2022
iloveflask is a Python library to collect functions that help a flask developer generate reports, config files and repeat code.

I Love Flask iloveflask is a Python library to collect functions that help a flask developer generate reports, config files and repeat code. Installat

2 Dec 29, 2021
A Flask extension that enables or disables features based on configuration.

Flask FeatureFlags This is a Flask extension that adds feature flagging to your applications. This lets you turn parts of your site on or off based on

Rachel Greenfield 131 Sep 26, 2022
SqlAlchemy Flask-Restful Swagger Json:API OpenAPI

SAFRS: Python OpenAPI & JSON:API Framework Overview Installation JSON:API Interface Resource Objects Relationships Methods Custom Methods Class Method

Thomas Pollet 365 Jan 06, 2023
A caching extension for Flask

Flask-Caching Adds easy cache support to Flask. This is a fork of the Flask-Cache extension. Flask-Caching also includes the cache module from werkzeu

Peter Justin 774 Jan 02, 2023
REST API with Flask and SQLAlchemy. I would rather not use it anymore.

Flask REST API Python 3.9.7 The Flask experience, without data persistence :D First, to install all dependencies: python -m pip install -r requirement

Luis Quiñones Requelme 1 Dec 15, 2021
This is a simple web application using Python Flask and MySQL database.

Simple Web Application This is a simple web application using Python Flask and MySQL database. This is used in the demonstration of development of Ans

Alaaddin Tarhan 1 Nov 16, 2021
Analytics snippets generator extension for the Flask framework.

Flask-Analytics Flask Analytics is an extension for Flask which generates analytics snippets for inclusion in templates. Installation $ pip install Fl

Mihir 80 Nov 30, 2022
Flask app + (html+css+ajax) contain ability add employee and place where employee work - plant or salon

#Manage your employees! With all employee information stored in one place, you no longer have to sift through hoards of spreadsheets to manually searc

Kateryna 1 Dec 22, 2021
YAML-formatted plain-text file based models for Flask backed by Flask-SQLAlchemy

Flask-FileAlchemy Flask-FileAlchemy is a Flask extension that lets you use Markdown or YAML formatted plain-text files as the main data store for your

Siddhant Goel 20 Dec 14, 2022
A live chat built with python(flask + gevent + apscheduler) + redis

a live chat room built with python(flask / gevent / apscheduler) + redis Basic Architecture Screenshot Install cd /path/to/source python bootstrap.py

Limboy 309 Nov 13, 2022
a flask zipkin extension based on py_zipkin.

flask-zipkin a flask zipkin extension based on py_zipkin. Installation pip install flask_zipkin usage you can simply use it as other flask extensions.

39 Jul 03, 2022
A simple FastAPI web service + Vue.js based UI over a rclip-style clip embedding database.

Explore CLIP Embeddings in a rclip database A simple FastAPI web service + Vue.js based UI over a rclip-style clip embedding database. A live demo of

18 Oct 15, 2022
Template for a rest app with flask, flask-rest and more...

Flask REST Template About the project (some comments): The ideia behind the project is to create an useful and simple template for an rest app . Besid

107 Nov 16, 2022
An extension to add support of Plugin in Flask.

An extension to add support of Plugin in Flask.

Doge Gui 31 May 19, 2022
Flask starter template for better structuring.

Flask Starter app Flask starter template for better structuring. use the starter plate step 1 : cloning this repo through git clone the repo git clone

Tirtharaj Sinha 1 Jul 26, 2022
A flask template with Bootstrap 4, asset bundling+minification with webpack, starter templates, and registration/authentication. For use with cookiecutter.

cookiecutter-flask A Flask template for cookiecutter. (Supports Python ≥ 3.6) See this repo for an example project generated from the most recent vers

4.3k Dec 29, 2022
Python web-app (Flask) to browse Tandoor recipes on the local network

RecipeBook - Tandoor Python web-app (Flask) to browse Tandoor recipes on the local network. Designed for use with E-Ink screens. For a version that wo

5 Oct 02, 2022
A web application made with Flask that works with a weather service API to get the current weather from all over the world.

Weather App A web application made with Flask that works with a weather service API to get the current weather from all over the world. Uses data from

Christian Jairo Sarmiento 19 Dec 02, 2022