Command Line Interface#

New in version 2.4.0.

Flask-Limiter adds a few subcommands to the Flask Command Line Interface for maintenance & diagnostic purposes. These can be accessed under the limiter sub-command as follows

Usage: flask limiter [OPTIONS] COMMAND [ARGS]...

  Flask-Limiter maintenance & utility commmands

Options:
  --help  Show this message and exit.

Commands:
  clear   Clear limits for a specific key
  config  View the extension configuration
  limits  Enumerate details about all routes with rate limits

Example#

The examples below use the following example application:

import os
import jinja2
from flask import Blueprint, Flask, jsonify, request, render_template, make_response
from flask.views import View

import flask_limiter
from flask_limiter import ExemptionScope, Limiter
from flask_limiter.util import get_remote_address


def index_error_responder(request_limit):
    error_template = jinja2.Environment().from_string(
        """
    <h1>Breached rate limit of: {{request_limit.limit}}</h1>
    <h2>Path: {{request.path}}</h2>
    """
    )
    return make_response(render_template(error_template, request_limit=request_limit))


def app():
    def default_limit_extra():
        if request.headers.get("X-Evil"):
            return "100/minute"
        return "200/minute"

    def default_cost():
        if request.headers.get("X-Evil"):
            return 2
        return 1

    limiter = Limiter(
        get_remote_address,
        default_limits=["20/hour", "1000/hour", default_limit_extra],
        default_limits_exempt_when=lambda: request.headers.get("X-Internal"),
        default_limits_deduct_when=lambda response: response.status_code == 200,
        default_limits_cost=default_cost,
        application_limits=["5000/hour"],
        headers_enabled=True,
        storage_uri=os.environ.get("FLASK_RATELIMIT_STORAGE_URI", "memory://"),
    )

    app = Flask(__name__)
    app.config.from_prefixed_env()

    @app.errorhandler(429)
    def handle_error(e):
        return e.get_response() or make_response(
            jsonify(error="ratelimit exceeded %s" % e.description)
        )

    @app.route("/")
    @limiter.limit("10/minute", on_breach=index_error_responder)
    def root():
        """
        Custom rate limit of 10/minute which overrides the default limits.
        The error page displayed on rate limit breached is also customized by using
        an `on_breach` callback to render a template
        """
        return "42"

    @app.route("/version")
    @limiter.exempt
    def version():
        """
        Exempt from all rate limits
        """
        return flask_limiter.__version__

    health_blueprint = Blueprint("health", __name__, url_prefix="/health")

    @health_blueprint.route("/")
    def health():
        return "ok"

    app.register_blueprint(health_blueprint)

    #: Exempt from default, application and ancestor rate limits (effectively all)
    limiter.exempt(
        health_blueprint,
        flags=ExemptionScope.DEFAULT
        | ExemptionScope.APPLICATION
        | ExemptionScope.ANCESTORS,
    )

    class ResourceView(View):
        methods = ["GET", "POST"]

        @staticmethod
        def json_error_responder(request_limit):
            return jsonify({"limit": str(request_limit.limit)})

        #: Custom rate limit of 5/second by http method type for all routes under this
        #: resource view. The error response is also customized by using the `on_breach`
        #: callback to return a json error response
        decorators = [
            limiter.limit("5/second", per_method=True, on_breach=json_error_responder)
        ]

        def dispatch_request(self):
            return request.method.lower()

    app.add_url_rule("/resource", view_func=ResourceView.as_view("resource"))

    limiter.init_app(app)

    return app


if __name__ == "__main__":
    app().run()

Extension Config#

Use the subcommand config to display the active configuration

$ flask limiter config
$ FLASK_APP=../../examples/kitchensink.py:app flask limiter config
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'

List limits#

$ flask limiter limits

Use the subcommand limits to display all configured limits

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter limits
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'

Filter by endpoint name#

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter limits --endpoint=root
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'

Filter by path#

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter limits --path=/health/
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'

Check limit status#

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter limits --key=127.0.0.1
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'

Clear limits#

$ flask limiter clear

The CLI exposes a subcommand clear that can be used to clear either all limits or limits for specific endpoints or routes by a key which represents the value returned by the key_func (i.e. a specific user) callable configured for your application.

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter clear --help
Usage: flask limiter clear [OPTIONS]

  Clear limits for a specific key

Options:
  --endpoint TEXT  Endpoint to filter by
  --path TEXT      Path to filter by
  --method TEXT    HTTP Method to filter by
  --key TEXT       Key to reset the limits for  [required]
  -y               Skip prompt for confirmation
  --help           Show this message and exit.

By default this is an interactive command which requires confirmation, however it can also be used in automations by using the -y flag to force confirmation.

$ FLASK_APP=../../examples/kitchensink.py:app flask limiter clear --key=127.0.0.1 -y
Traceback (most recent call last):
  File "/usr/bin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==2.0.3', 'console_scripts', 'flask')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 995, in main
    cli.main(args=sys.argv[1:])
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 601, in main
    return super().main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 444, in decorator
    with __ctx.ensure_object(ScriptInfo).load_app().app_context():
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 407, in load_app
    app = locate_app(self, import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 279, in locate_app
    return find_app_by_string(script_info, module, app_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 205, in find_app_by_string
    app = call_factory(script_info, attr, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/flask/cli.py", line 123, in call_factory
    return app_factory(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/build/flask-limiter-lz9Q4W/flask-limiter-3.1.0/examples/kitchensink.py", line 44, in app
    app.config.from_prefixed_env()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Config' object has no attribute 'from_prefixed_env'