How to Implement Rate Limiting in FastAPI

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints. In real - world applications, especially those exposed to the public, it’s crucial to control the rate at which clients can make requests. Rate limiting helps prevent abuse, such as denial - of - service (DoS) attacks, and ensures fair usage of resources. This blog will guide you through the process of implementing rate limiting in FastAPI, covering fundamental concepts, usage methods, common practices, and best practices.

Table of Contents

  1. Fundamental Concepts of Rate Limiting
  2. Setting up a Basic FastAPI Application
  3. Implementing Rate Limiting with slowapi
    • Installation
    • Basic Configuration
    • Applying Rate Limits to Routes
  4. Common Practices
    • IP - Based Rate Limiting
    • User - Based Rate Limiting
  5. Best Practices
    • Handling Rate Limit Exceeded
    • Scaling Rate Limiting
  6. Conclusion
  7. References

1. Fundamental Concepts of Rate Limiting

Rate limiting is a strategy for limiting network traffic. It restricts the number of requests that a client can make to a server within a specified time frame. There are different types of rate - limiting algorithms, such as fixed window, sliding window, and leaky bucket.

  • Fixed Window: Divides time into fixed intervals (e.g., every minute). A client can make a certain number of requests within each interval. Once the limit is reached, further requests are blocked until the next interval.
  • Sliding Window: A more flexible approach that keeps track of requests over a moving time window. It provides a smoother rate - limiting experience compared to the fixed window.
  • Leaky Bucket: Treats requests as water flowing into a bucket. The bucket has a fixed capacity, and requests are processed at a constant rate. If the bucket overflows, additional requests are rejected.

2. Setting up a Basic FastAPI Application

First, make sure you have FastAPI and Uvicorn installed. You can install them using pip:

pip install fastapi uvicorn

Here is a simple FastAPI application:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}

To run this application, use the following command:

uvicorn main:app --reload

3. Implementing Rate Limiting with slowapi

Installation

slowapi is a popular library for implementing rate limiting in Python web applications. Install it using pip:

pip install slowapi

Basic Configuration

from fastapi import FastAPI
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
limiter.state.limiter._default_limits = ["5/minute"]
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)


@app.get("/")
@limiter.limit("5/minute")
def read_root():
    return {"Hello": "World"}

Applying Rate Limits to Routes

In the above example, we applied a rate limit of 5 requests per minute to the root route (/). The get_remote_address function is used to identify clients based on their IP addresses.

4. Common Practices

IP - Based Rate Limiting

As shown in the previous example, we can use the client’s IP address to enforce rate limits. This is useful for preventing abuse from a single IP address.

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

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)


@app.get("/ip - limited")
@limiter.limit("3/minute")
def ip_limited_route(request: Request):
    return {"message": "This route is IP - based rate limited"}

User - Based Rate Limiting

If your application has user authentication, you can use the user ID to enforce rate limits.

from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded

# Assume we have a function to get user ID
def get_user_id(request: Request):
    # Replace this with actual user ID retrieval logic
    return "user123"

limiter = Limiter(key_func=get_user_id)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)


@app.get("/user - limited")
@limiter.limit("2/minute")
def user_limited_route(request: Request):
    return {"message": "This route is user - based rate limited"}

5. Best Practices

Handling Rate Limit Exceeded

When a client exceeds the rate limit, it’s important to provide a meaningful response. The slowapi library provides a default handler, but you can customize it.

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

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter


def custom_rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded):
    return {"detail": f"Rate limit exceeded: {exc.detail}"}


app.add_exception_handler(RateLimitExceeded, custom_rate_limit_exceeded_handler)


@app.get("/custom - limit")
@limiter.limit("1/minute")
def custom_limit_route():
    return {"message": "This route has a custom rate limit handler"}

Scaling Rate Limiting

For large - scale applications, you may need to use a distributed rate - limiting solution. Redis is a popular choice for implementing distributed rate limits. You can use libraries like slowapi - redis to integrate Redis with slowapi.

pip install slowapi - redis
from fastapi import FastAPI
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.middleware import SlowAPIMiddleware
from slowapi_redis import RedisStorage
import redis

redis_client = redis.Redis(host='localhost', port=6379, db=0)
storage = RedisStorage(redis_client)
limiter = Limiter(key_func=get_remote_address, storage=storage)
app = FastAPI()
app.add_middleware(SlowAPIMiddleware)


@app.get("/distributed - limit")
@limiter.limit("10/minute")
def distributed_limit_route():
    return {"message": "This route uses distributed rate limiting"}

6. Conclusion

Rate limiting is an essential feature for any web application exposed to the public. In FastAPI, you can easily implement rate limiting using libraries like slowapi. By understanding the fundamental concepts, common practices, and best practices, you can protect your application from abuse and ensure fair resource usage. Whether you are using IP - based or user - based rate limits, and whether you need to scale your rate - limiting solution, there are tools and techniques available to meet your needs.

7. References