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 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
A template themes for phyton flask website

Flask Phyton Web template A template themes for phyton flask website

Mesin Kasir 2 Nov 29, 2021
flask extension for integration with the awesome pydantic package

Flask-Pydantic Flask extension for integration of the awesome pydantic package with Flask. Installation python3 -m pip install Flask-Pydantic Basics v

249 Jan 06, 2023
A solid foundation for your flask app

Flask Foundation There is a cookiecutter version of this repo at https://github.com/JackStouffer/cookiecutter-Flask-Foundation. Documentation is locat

Jack Stouffer 1.3k Dec 11, 2022
A simple Task todo application built with Flask

Task TODO Table An application built with Flask a Python framework and hosted on Heroku. Important notes GuniCorn (Green Unicorn): is a Python WSGI HT

DCN (Dubem Celestine Nwabuisi) 1 Dec 15, 2021
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
Full Stack Web Development with Flask.

Discover Flask Full Stack Web Development with Flask. http://discoverflask.com Flask is a micro web framework powered by Python. Its API is fairly sma

Real Python 4.4k Jan 06, 2023
Rich implementation for Flask

Flask Rich Implements the Rich programming library with Flask. All features are toggleable, including: Better logging Colorful tracebacks Usage Import

BD103 13 Jun 06, 2022
Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário

Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário O pyautogui foi escolhido pela possibilidade de fazer a identificação do

Rodrigo Vital 2 Oct 20, 2021
Flask-Starter is a boilerplate starter template designed to help you quickstart your Flask web application development.

Flask-Starter Flask-Starter is a boilerplate starter template designed to help you quickstart your Flask web application development. It has all the r

Kundan Singh 259 Dec 26, 2022
A fairly common feature in web applications to have links that open a popover when hovered

Add Popovers to Links in Flask App It is a fairly common feature in web applications to have links that open a popover when hovered. Twitter does this

Gitau Harrison 1 Jan 22, 2022
Flaskr: Intro to Flask, Test-Driven Development (TDD), and JavaScript

Flaskr - Intro to Flask, Test-Driven Development, and JavaScript Share on Twitter As many of you know, Flaskr -- a mini-blog-like-app -- is the app th

Michael Herman 2.2k Jan 04, 2023
Sample Dockerized flask app deployed on Kubernetes on Azure using AKS

Sample Dockerized flask app deployed on Kubernetes on Azure using AKS

Ahmed khémiri 22 Sep 08, 2021
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
Browsable web APIs for Flask.

Flask API Browsable web APIs for Flask. Status: This project is in maintenance mode. The original author (Tom Christie) has shifted his focus to API S

Flask API 1.3k Jan 05, 2023
A flask app that turn image into ASCII art

ASCII art A flask app that turn image into ASCII art. This app has been deployed to https://motmaytinh.herokuapp.com Getting Started These instruction

Trần Ngọc Quý 1 Jan 13, 2022
An easy way to build your flask skeleton.

Flider What is Flider Flider is a lightweight framework that saves you time by creating a MVC compliant file structure and includes basic commonly use

Trevor Engen 8 Nov 17, 2022
Making a simple app using React, Flask and MySQL.

Samys-Cookbook Making a simple app using React and Flask. What This will be a simple site to host my recipes. It will have a react front-end, a flask

Samridh Anand Paatni 1 Jul 07, 2022
A simple way to demo Flask apps from your machine.

flask-ngrok A simple way to demo Flask apps from your machine. Makes your Flask apps running on localhost available over the internet via the excellen

117 Dec 27, 2022
Flask Apps - Open-Source And Paid | AppSeed

Flask Apps Open-Source web apps built with automation tools, HTML parsing and boilerplated code in Flask - - Provided by AppSeed App Generator. What i

App Generator 120 Oct 04, 2022