API security is a crucial part of application development, and one of the most overlooked vulnerabilities is Abuse through Excessive Requests.
That means your API is maliciously used by methods that are outside of the intended or acceptable use cases.
Without proper safeguards, your API could be hammered by bots or scrapers, leading to downtime and inflated costs.
One solution: Rate Limiting.
Rate limiting refers to a technique used to control the number of requests a client (e.g., a user, application, or IP address) can make to an API or service within a specific time period.
This isn't just about blocking bad actors, it's about fairness.
It ensures your API remains stable and responsive for all users.
Python Implementation of Rate Limiting
In Python, we can use slowapi
for FastAPI or Starlette applications.
slowapi
is a Python library for rate limiting your API in a simple way.
Let's look how to implement it with FastAPI.
Install necessary dependencies:
slowapi==0.1.9
fastapi==0.115.8
uvicorn==0.34.0
And create our simple app.
First we make our necessary imports:
# main.py
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
Of course, FastAPI
needs to be imported, but also some classes and functions from slowapi
.
Now, we define our rate limiter and add it to our FastAPI
app:
limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
We defined our Limiter
instance with the function, which should be used as a base for the rate limiting.
In this case, get_remote_address
is a utility function which returns the IP address for the current request.
So, limiting is based on the IP address.
Then, we need to make the limiter
available to the FastAPI app so that it can be used by the middleware and decorators from slowapi
.
After that, we add an exception handler when limits are hit, which will return a 429 error.
Now, we add an endpoint with a rate limit:
@app.get("/limited")
@limiter.limit("1/minute")
async def limited_endpoint(request: Request):
return {"message": "Limited endpoint (1 requests/min)"}
We add a decorator from slowapi
to limit this endpoint to 1 request per minute.
Note: It is important to pass request: Request
to the function, or slowapi
is not able to work.
Let's try it out. Spin up your app
uvicorn run main:app --port 8000
And make a request to the endpoint
curl http://127.0.0.1:8000/limited
You will get
{"message":"Limited endpoint (1 requests/min)"}
But, when you run the curl
command again, you will get:
{"error":"Rate limit exceeded: 1 per 1 minute"}
It worked!
Conclusion
slowapi
makes implementing rate limiting for your API incredibly straightforward. While IP-based rate limiting is a common approach, slowapi
offers the flexibility to enforce limits based on virtually any criteria.
For instance, you can extract a user ID from a JWT token and apply rate limits on a per-user basis. Wrap it into a function and pass it to the Limiter
class.
This is particularly useful for APIs where users have varying access levels or subscription tiers.
By leveraging slowapi
's customizable key_func
, you can tailor rate limiting to your specific needs.
Whether it’s per IP, per user, or even per API key. This ensures your API remains secure, fair, and scalable, no matter how complex your requirements are.