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)
A Fast API style support for Flask. Gives you MyPy types with the flexibility of flask

Flask-Fastx Flask-Fastx is a Fast API style support for Flask. It Gives you MyPy types with the flexibility of flask. Compatibility Flask-Fastx requir

Tactful.ai 18 Nov 26, 2022
Seamlessly serve your static assets of your Flask app from Amazon S3

flask-s3 Seamlessly serve the static assets of your Flask app from Amazon S3. Maintainers Flask-S3 is maintained by @e-dard, @eriktaubeneck and @SunDw

Edd Robinson 188 Aug 24, 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
Full-Stack application that visualizes amusement park safety.

Amusement Park Ride Safety Analysis Project Proposal We have chosen to look into amusement park data to explore ride safety relationships visually, in

Michael Absher 0 Jul 11, 2021
Flask Project Template A full feature Flask project template.

Flask Project Template A full feature Flask project template. See also Python-Project-Template for a lean, low dependency Python app. HOW TO USE THIS

Bruno Rocha 96 Dec 23, 2022
Basic flask system for login, api, and status system

Flask Multi Site With Login This is a basic website login system with an uncomplete api system NOTICE : This is NOT complete and is made to be a bare

MrShoe 0 Feb 02, 2022
A Flask application for Subdomain Enumeration

subdomainer-flask A Flask application for Subdomain Enumeration steps to be done git clone https://github.com/gokulapap/subdomainer-flask pip3 install

GOKUL A.P 7 Sep 22, 2022
A simple example using Flask inside a container

This is a simple example of how create a container for a Python Flask Web Application using Docker.

Fazt Web 8 Aug 30, 2022
A weather report application build with Python, Flask, and Geopy.

A weather report application build with Python, Flask, and Geopy. Requirements Python 3

Brandon Wallace 6 May 07, 2022
This repo contains the Flask API to expose model and get predictions.

Tea Leaf Quality Srilanka Chapter This repo contains the Flask API to expose model and get predictions. Expose Model As An API Model Trainig will happ

DuKe786 2 Nov 12, 2021
Serve angular production application from python flask backend. Quick and Easy

Serve angular production application from python flask backend. Quick and Easy

mark 1 Dec 01, 2022
OpenTracing instrumentation for the Flask microframework

Flask-OpenTracing This package enables distributed tracing in Flask applications via The OpenTracing Project. Once a production system contends with r

3rd-Party OpenTracing API Contributions 133 Dec 19, 2022
Flask 文档中文翻译

Flask 中文文档 这里是 Flask 文档中文翻译项目,欢迎参与! 在开始翻译之前,请务必阅读下面的 Contributing Guide 了解贡献流程,然后阅读这个 Issue 了解翻译要求,在这个 Discussion 投票选出你认为合适的翻译词汇,在这个 Discussion 投票选出你喜

Grey Li 93 Nov 28, 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
retorna informações de pessoas que não existem

random-person-api API que entrega dados aleatórios sobre pessoas que (provavelmente) não existem. Como usar? Copie o link abaixo https://random-person

Miguel 3 Aug 09, 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 flask extension using pyexcel to read, manipulate and write data in different excel formats: csv, ods, xls, xlsx and xlsm.

Flask-Excel - Let you focus on data, instead of file formats Support the project If your company has embedded pyexcel and its components into a revenu

247 Dec 27, 2022
Adds Injector support to Flask.

Flask-Injector Adds Injector support to Flask, this way there's no need to use global Flask objects, which makes testing simpler. Injector is a depend

Alec Thomas 246 Dec 28, 2022
Control YouTube, streaming sites, media players on your computer using your phone as a remote.

Media Control Control Youtube, streaming sites, media players on your computer using your phone as a remote. Installation pip install -r requirements.

Shreyas Daniel 10 Dec 08, 2022
Flask RESTful Web services using API to communicate between client and server.

Welcome! Open up two terminals, one for client and for server each Terminal 1 Terminal 2 Now navigate to the CW2_code directory in both like so $ cd C

Sehra Elahi 1 Nov 23, 2021