Flask-Limiter
Flask-Limiter is an active Python extension that adds rate limiting capabilities to Flask applications, preventing abuse and ensuring stability. It allows configuration of limits at various levels (application-wide, per Blueprint, per route) and supports multiple storage backends like Redis, Memcached, MongoDB, and Valkey. The current version is 4.1.1, with a regular release cadence.
Warnings
- breaking Version 4.0.0 introduced significant breaking changes in module structure and limit definition. All internal submodules are now prefixed with an underscore, and direct imports from them (e.g., `from flask_limiter.limits import Limit`) are deprecated. Instead, import classes like `Limit`, `RouteLimit`, `ApplicationLimit`, and `MetaLimit` directly from the root `flask_limiter` namespace.
- breaking Version 3.0.0 changed the `Limiter` constructor arguments. `key_func` is now a mandatory positional argument, and all other arguments must be passed as keyword arguments. The `RATELIMIT_STORAGE_URL` configuration variable was removed, and legacy Flask < 2 compatibility was dropped.
- gotcha Using in-memory storage (`memory://`) in production with multiple worker processes will lead to inaccurate and unreliable rate limiting. Each worker will maintain its own independent limit state, making global rate limits ineffective.
- deprecated The 3.13 release was yanked from PyPI due to compatibility issues with Flask-AppBuilder and Airflow. Users who installed this specific version might encounter unexpected behavior or errors.
- breaking Flask-Limiter frequently adjusts its supported Python versions. For example, Python 3.9 support was dropped in v3.12, and Python 3.8 support was dropped in v3.9.0. Currently, Python >=3.10 is required.
- gotcha When deploying behind a proxy (e.g., Nginx, Gunicorn), `get_remote_address` might return the proxy's IP address instead of the client's. This can lead to all requests being limited by the proxy's IP, effectively acting as a single global limit for all users.
Install
-
pip install Flask-Limiter -
pip install Flask-Limiter[redis] -
pip install Flask-Limiter[memcached] -
pip install Flask-Limiter[mongodb] -
pip install Flask-Limiter[valkey] -
pip install Flask-Limiter[cli]
Imports
- Limiter
from flask_limiter import Limiter
- get_remote_address
from flask_limiter.util import get_remote_address
- Limit
from flask_limiter import Limit
Quickstart
import os
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
# Configure storage_uri, using in-memory for example, or from an environment variable
# In-memory storage is for development/testing only and should not be used in production with multiple workers.
# For production, use backends like Redis: 'redis://localhost:6379'
storage_uri = os.environ.get('FLASK_RATELIMIT_STORAGE_URI', 'memory://')
limiter = Limiter(
key_func=get_remote_address,
app=app,
default_limits=["200 per day", "50 per hour"],
storage_uri=storage_uri,
strategy="fixed-window" # Or 'moving-window', 'sliding-window-counter'
)
@app.route("/slow")
@limiter.limit("1 per day")
def slow():
return ":("
@app.route("/medium")
@limiter.limit("1/second", override_defaults=False)
def medium():
return ":|"
@app.route("/fast")
def fast():
return ":)"
@app.route("/ping")
@limiter.exempt
def ping():
return "PONG"
# Example error handler for rate limit exceeded (HTTP 429)
@app.errorhandler(429)
def ratelimit_handler(e):
return f"Rate limit exceeded: {e.description}", 429
if __name__ == '__main__':
app.run(debug=True)