Flask-Limiter
raw JSON → 4.1.1 verified Tue May 12 auth: no python install: verified
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.
pip install Flask-Limiter Common errors
error TypeError: Limiter.__init__() got multiple values for argument 'key_func' ↓
cause This error occurs when the 'app' instance is passed as a positional argument after 'key_func' during Limiter initialization, causing Python to interpret 'app' as a second value for 'key_func' because 'key_func' is the only positional argument.
fix
Pass the 'app' instance using the keyword argument
app=app during Limiter initialization. For example: limiter = Limiter(key_func=get_remote_address, app=app, default_limits=['200 per day']). error ModuleNotFoundError: No module named 'flask_limiter.wrappers' ↓
cause The 'flask_limiter.wrappers' module was removed as a breaking change in Flask-Limiter version 3.13, causing applications that directly import or rely on this module (especially older versions of dependent libraries like Flask-AppBuilder or Apache Superset) to fail.
fix
Pin your 'flask-limiter' dependency to a version prior to 3.13 (e.g.,
flask-limiter==3.12.1) or update any dependent libraries to versions compatible with newer 'flask-limiter' releases. error flask-limiter redis not working ↓
cause This issue, or similar 'time out' errors, typically arises when the specified storage backend (like Redis, Memcached, or MongoDB) is not running, is misconfigured (e.g., incorrect `storage_uri`), or the necessary Python client library for that backend has not been installed as an extra dependency.
fix
Ensure the chosen storage backend service is running and accessible, verify the
storage_uri is correctly formatted (e.g., redis://localhost:6379), and install the required extra dependencies for your backend (e.g., pip install Flask-Limiter[redis]). error AttributeError: module 'configparser' has no attribute 'SafeConfigParser' ↓
cause This error occurs because 'SafeConfigParser' was deprecated and removed in Python 3.10 and later, and an older version of 'flask-limiter' or its underlying 'limits' library attempts to use it.
fix
Upgrade 'flask-limiter' and its 'limits' dependency to versions compatible with Python 3.10+ (typically
flask-limiter>=2.0.0 and limits>=2.0.0). 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. ↓
fix Update imports to use `from flask_limiter import ClassName` (e.g., `from flask_limiter import Limit`) and adjust how limits are configured, leveraging the new limit description classes.
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. ↓
fix Ensure `key_func` is the first argument in `Limiter()` and all subsequent arguments are explicitly named (e.g., `Limiter(get_remote_address, app=app, storage_uri='...')`). Replace `RATELIMIT_STORAGE_URL` with `storage_uri` or `RATELIMIT_STORAGE_URI` in Flask config.
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. ↓
fix Always configure a persistent storage backend (e.g., Redis, Memcached, MongoDB, Valkey) for production deployments by setting `storage_uri` or the `RATELIMIT_STORAGE_URI` Flask config variable.
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. ↓
fix Avoid using version 3.13. If you are on 3.13, downgrade to a stable 3.x release (e.g., 3.12 or earlier) or upgrade to version 4.0.0 or later.
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. ↓
fix Always check the `requires_python` metadata (or `py_modules` in the PyPI classifiers) for the specific Flask-Limiter version you intend to use and ensure your Python environment meets the requirements.
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. ↓
fix Properly configure your proxy to forward the client's IP in a header (e.g., `X-Forwarded-For`) and configure Flask-Limiter to use this header, potentially with a custom `key_func` or by configuring `RATELIMIT_HEADERS_ENABLED` and `RATELIMIT_HEADER_ID`.
Install
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] Install compatibility verified last tested: 2026-05-12
python os / libc variant status wheel install import disk
3.10 alpine (musl) Flask-Limiter wheel - 0.60s 25.1M
3.10 alpine (musl) Flask-Limiter - - 0.58s 24.9M
3.10 alpine (musl) cli wheel - 0.65s 37.3M
3.10 alpine (musl) cli - - 0.58s 37.1M
3.10 alpine (musl) memcached wheel - 0.62s 25.7M
3.10 alpine (musl) memcached - - 0.58s 25.5M
3.10 alpine (musl) mongodb sdist - 0.56s 33.1M
3.10 alpine (musl) mongodb - - 0.57s 32.9M
3.10 alpine (musl) redis wheel - 0.64s 28.9M
3.10 alpine (musl) redis - - 0.61s 28.7M
3.10 alpine (musl) valkey wheel - 0.62s 27.4M
3.10 alpine (musl) valkey - - 0.58s 27.3M
3.10 slim (glibc) Flask-Limiter wheel 2.9s 0.46s 26M
3.10 slim (glibc) Flask-Limiter - - 0.45s 25M
3.10 slim (glibc) cli wheel 3.9s 0.47s 38M
3.10 slim (glibc) cli - - 0.44s 38M
3.10 slim (glibc) memcached wheel 3.0s 0.44s 26M
3.10 slim (glibc) memcached - - 0.44s 26M
3.10 slim (glibc) mongodb wheel 4.1s 0.44s 35M
3.10 slim (glibc) mongodb - - 0.45s 35M
3.10 slim (glibc) redis wheel 3.2s 0.45s 29M
3.10 slim (glibc) redis - - 0.45s 29M
3.10 slim (glibc) valkey wheel 3.1s 0.44s 28M
3.10 slim (glibc) valkey - - 0.46s 28M
3.11 alpine (musl) Flask-Limiter wheel - 0.77s 28.1M
3.11 alpine (musl) Flask-Limiter - - 0.87s 27.9M
3.11 alpine (musl) cli wheel - 0.74s 41.5M
3.11 alpine (musl) cli - - 0.85s 41.3M
3.11 alpine (musl) memcached wheel - 0.76s 28.9M
3.11 alpine (musl) memcached - - 0.87s 28.6M
3.11 alpine (musl) mongodb sdist - 0.74s 37.7M
3.11 alpine (musl) mongodb - - 0.87s 37.3M
3.11 alpine (musl) redis wheel - 0.75s 32.7M
3.11 alpine (musl) redis - - 0.87s 32.4M
3.11 alpine (musl) valkey wheel - 0.74s 30.9M
3.11 alpine (musl) valkey - - 0.84s 30.7M
3.11 slim (glibc) Flask-Limiter wheel 2.9s 0.68s 29M
3.11 slim (glibc) Flask-Limiter - - 0.67s 28M
3.11 slim (glibc) cli wheel 4.0s 0.70s 42M
3.11 slim (glibc) cli - - 0.66s 42M
3.11 slim (glibc) memcached wheel 3.0s 0.69s 29M
3.11 slim (glibc) memcached - - 0.65s 29M
3.11 slim (glibc) mongodb wheel 3.8s 0.68s 41M
3.11 slim (glibc) mongodb - - 0.65s 40M
3.11 slim (glibc) redis wheel 3.2s 0.68s 33M
3.11 slim (glibc) redis - - 0.64s 33M
3.11 slim (glibc) valkey wheel 3.1s 0.74s 31M
3.11 slim (glibc) valkey - - 0.66s 31M
3.12 alpine (musl) Flask-Limiter wheel - 0.92s 19.8M
3.12 alpine (musl) Flask-Limiter - - 0.99s 19.5M
3.12 alpine (musl) cli wheel - 0.94s 33.0M
3.12 alpine (musl) cli - - 0.97s 32.7M
3.12 alpine (musl) memcached wheel - 0.89s 20.4M
3.12 alpine (musl) memcached - - 1.01s 20.2M
3.12 alpine (musl) mongodb sdist - 1.02s 29.1M
3.12 alpine (musl) mongodb - - 0.98s 28.8M
3.12 alpine (musl) redis wheel - 0.93s 24.1M
3.12 alpine (musl) redis - - 1.00s 23.9M
3.12 alpine (musl) valkey wheel - 1.00s 22.4M
3.12 alpine (musl) valkey - - 0.99s 22.2M
3.12 slim (glibc) Flask-Limiter wheel 2.6s 0.92s 20M
3.12 slim (glibc) Flask-Limiter - - 0.93s 20M
3.12 slim (glibc) cli wheel 3.5s 0.94s 34M
3.12 slim (glibc) cli - - 0.91s 33M
3.12 slim (glibc) memcached wheel 2.6s 0.91s 21M
3.12 slim (glibc) memcached - - 0.96s 21M
3.12 slim (glibc) mongodb wheel 3.3s 0.93s 33M
3.12 slim (glibc) mongodb - - 0.90s 32M
3.12 slim (glibc) redis wheel 2.9s 0.90s 25M
3.12 slim (glibc) redis - - 0.96s 24M
3.12 slim (glibc) valkey wheel 2.7s 0.92s 23M
3.12 slim (glibc) valkey - - 1.02s 23M
3.13 alpine (musl) Flask-Limiter wheel - 0.91s 19.5M
3.13 alpine (musl) Flask-Limiter - - 0.95s 19.2M
3.13 alpine (musl) cli wheel - 0.92s 32.7M
3.13 alpine (musl) cli - - 0.97s 32.4M
3.13 alpine (musl) memcached wheel - 0.90s 20.2M
3.13 alpine (musl) memcached - - 0.99s 19.8M
3.13 alpine (musl) mongodb sdist - 0.96s 28.8M
3.13 alpine (musl) mongodb - - 0.95s 28.4M
3.13 alpine (musl) redis wheel - 0.93s 23.8M
3.13 alpine (musl) redis - - 1.01s 23.5M
3.13 alpine (musl) valkey wheel - 0.90s 22.1M
3.13 alpine (musl) valkey - - 0.94s 21.8M
3.13 slim (glibc) Flask-Limiter wheel 2.7s 0.87s 20M
3.13 slim (glibc) Flask-Limiter - - 0.97s 20M
3.13 slim (glibc) cli wheel 3.5s 0.87s 33M
3.13 slim (glibc) cli - - 0.95s 33M
3.13 slim (glibc) memcached wheel 2.7s 0.87s 21M
3.13 slim (glibc) memcached - - 0.96s 20M
3.13 slim (glibc) mongodb wheel 3.5s 0.86s 33M
3.13 slim (glibc) mongodb - - 0.93s 33M
3.13 slim (glibc) redis wheel 2.9s 0.85s 24M
3.13 slim (glibc) redis - - 0.93s 24M
3.13 slim (glibc) valkey wheel 2.7s 0.88s 23M
3.13 slim (glibc) valkey - - 0.93s 22M
3.9 alpine (musl) Flask-Limiter wheel - 0.51s 35.9M
3.9 alpine (musl) Flask-Limiter - - 0.54s 35.9M
3.9 alpine (musl) cli wheel - 0.52s 35.9M
3.9 alpine (musl) cli - - 0.54s 35.9M
3.9 alpine (musl) memcached wheel - 0.54s 36.5M
3.9 alpine (musl) memcached - - 0.55s 36.5M
3.9 alpine (musl) mongodb sdist - 0.50s 43.8M
3.9 alpine (musl) mongodb - - 0.53s 43.7M
3.9 alpine (musl) redis wheel - 0.52s 38.8M
3.9 alpine (musl) redis - - 0.57s 38.7M
3.9 alpine (musl) valkey wheel - 0.53s 35.9M
3.9 alpine (musl) valkey - - 0.54s 35.9M
3.9 slim (glibc) Flask-Limiter wheel 4.8s 0.50s 36M
3.9 slim (glibc) Flask-Limiter - - 0.45s 36M
3.9 slim (glibc) cli wheel 4.8s 0.53s 36M
3.9 slim (glibc) cli - - 0.46s 36M
3.9 slim (glibc) memcached wheel 4.8s 0.47s 37M
3.9 slim (glibc) memcached - - 0.46s 37M
3.9 slim (glibc) mongodb wheel 6.0s 0.49s 45M
3.9 slim (glibc) mongodb - - 0.47s 45M
3.9 slim (glibc) redis wheel 5.2s 0.48s 39M
3.9 slim (glibc) redis - - 0.47s 39M
3.9 slim (glibc) valkey wheel 4.9s 0.49s 36M
3.9 slim (glibc) valkey - - 0.47s 36M
Imports
- Limiter
from flask_limiter import Limiter - get_remote_address
from flask_limiter.util import get_remote_address - Limit wrong
from flask_limiter.limits import Limitcorrectfrom flask_limiter import Limit
Quickstart last tested: 2026-04-24
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)