By default, exceeding a limit raises a RateLimitExceeded exception, resulting in a 429 Too Many Requests response. You can customize this behavior in two ways:
1. Global Error Handler
Register a standard Flask error handler for the 429 status code. This is the simplest way to return JSON instead of HTML for all routes.
2. Using on_breach callback
Provide an on_breach callback to the Limiter constructor or the @limiter.limit decorator. The callback receives a RequestLimit object and should return a flask.Response instance.
Priority Rules:
- If a specific route has an
on_breach callback defined via @limiter.limit, it takes priority over the global on_breach callback defined in the Limiter constructor. - If you have both an
on_breach callback AND a Flask @app.errorhandler(429), the error handler will be called. To ensure the on_breach response is used, your error handler should check error.get_response() first.
Note: Since version 2.8.0, errors in the on_breach callback are re-raised unless swallow_errors=True is set in the Limiter configuration.
# Global error handler approach
@app.errorhandler(429)
def ratelimit_handler(e):
return make_response(
jsonify(error=f"ratelimit exceeded {e.description}")
, 429
)
# Using on_breach callback (Global)
from flask_limiter import Limiter, RequestLimit
def default_error_responder(request_limit: RequestLimit):
return make_response(
render_template("my_ratelimit_template.tmpl", request_limit=request_limit),
429
)
app = Limiter(
key_func=...,
default_limits=["100/minute"],
on_breach=default_error_responder
)
# Using on_breach callback (Route-specific)
@app.route("/")
@limiter.limit("10/minute", on_breach=index_ratelimit_error_responder)
def index():
...