skip to Main Content

How to ratelimit API endpoint request in Fastapi application ? I need to ratelimit API call 5 request per second per user and exceeding that limit blocks that particular user for 60 seconds.

In main.py

def get_application() -> FastAPI:
     application = FastAPI(title=PROJECT_NAME, debug=DEBUG, version=VERSION)
     application.add_event_handler(
        "startup", create_start_app_handler(application))
     application.add_event_handler(
        "shutdown", create_stop_app_handler(application))
     return application
app = get_application()

In events.py

def create_start_app_handler(app: FastAPI) -> Callable:  
    async def start_app() -> None:           

        redis = await aioredis.create_redis_pool("redis://localhost:8080")
        FastAPILimiter.init(redis)
    return start_app

In endpoint

@router.post('/user',
             tags=["user"],
             name="user:user", dependencies=[Depends(RateLimiter(times=5, seconds=60))])
***code****

Run from this file test.py.

import uvicorn

from app.main import app

if __name__ == "__main__":
    uvicorn.run("test:app", host="0.0.0.0", port=8000, reload=True)

I edited as above but got following error.

File "****ite-packagesstarletterouting.py", line 526, in lifespan
    async for item in self.lifespan_context(app):
  File "****site-packagesstarletterouting.py", line 467, in default_lifespan
    await self.startup()
  File "****site-packagesstarletterouting.py", line 502, in startup
    await handler()
  File "****appcoreservicesevents.py", line 15, in start_app
    redis = await aioredis.create_redis_pool("redis://localhost:8080")
  File "****site-packagesaiorediscommands__init__.py", line 188, in create_redis_pool
    pool = await create_pool(address, db=db,
  File "****site-packagesaioredispool.py", line 58, in create_pool
    await pool._fill_free(override_min=False)
  File "C****site-packagesaioredispool.py", line 383, in _fill_free
    conn = await self._create_new_connection(self._address)
  File "****site-packagesaioredisconnection.py", line 111, in create_connection
    reader, writer = await asyncio.wait_for(open_connection(
  File "****asynciotasks.py", line 455, in wait_for
    return await fut
  File "****site-packagesaioredisstream.py", line 23, in open_connection
    transport, _ = await get_event_loop().create_connection(
  File "****asynciobase_events.py", line 1033, in create_connection
    raise OSError('Multiple exceptions: {}'.format(
OSError: Multiple exceptions: [Errno 10061] Connect call failed ('::1', 8080, 0, 0), [Errno 10061] Connect call failed ('127.0.0.1', 8080)

4

Answers


  1. FastAPI doesn’t natively support this, but it’s possible with a few libraries such the ones below, but will usually require some sort of database backing(redis, memcached, etc), although slowapi has a memory fallback in case of no database.

    In order to use fastapi-limiter, as seen in their documentation:

    Note: You will need a running Redis for this to work.

    import aioredis
    import uvicorn
    from fastapi import Depends, FastAPI
    
    from fastapi_limiter import FastAPILimiter
    from fastapi_limiter.depends import RateLimiter
    
    app = FastAPI()
    
    
    @app.on_event("startup")
    async def startup():
        redis = await aioredis.create_redis_pool("redis://localhost")
        FastAPILimiter.init(redis)
    
    
    @app.get("/", dependencies=[Depends(RateLimiter(times=2, seconds=5))])
    async def index():
        return {"msg": "Hello World"}
    
    
    if __name__ == "__main__":
        uvicorn.run("main:app", debug=True, reload=True)
    
    Login or Signup to reply.
  2. Best option is using a library since FastAPI does not provide this functionality out-of-box.

    slowapi is great, and easy to use.

    You can use ut like this.

    from fastapi import FastAPI
    from slowapi.errors import RateLimitExceeded
    from slowapi import Limiter, _rate_limit_exceeded_handler
    from slowapi.util import get_remote_address
    
    
    limiter = Limiter(key_func=get_remote_address)
    app = FastAPI()
    app.state.limiter = limiter
    app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
    
    @app.get("/home")
    @limiter.limit("5/minute")
    async def homepage(request: Request):
        return PlainTextResponse("test")
    
    @app.get("/mars")
    @limiter.limit("5/minute")
    async def homepage(request: Request, response: Response):
        return {"key": "value"}
    
    Login or Signup to reply.
  3. fastapi-limiter and slowapi is very beautiful package to implement Ratelimit in Fastapi.

    but use walrus can also do it. but should start redis database.

    1. start a redis.

    2. python code: write a python file: code1228.py

    3. code:

    from walrus import Database, RateLimitException
    from fastapi import FastAPI, Request
    from fastapi.responses import JSONResponse
    import uvicorn
    
    db = Database()
    rate = db.rate_limit('xxx', limit=5, per=60)  # in 60s just can only click 5 times
    
    app = FastAPI()
    
    
    @app.exception_handler(RateLimitException)
    def parse_rate_litmit_exception(request: Request, exc: RateLimitException):
        msg = {'success': False, 'msg': f'please have a tea for sleep, your ip is: {request.client.host}.'}
        return JSONResponse(status_code=429, content=msg)
    
    
    @app.get('/')
    def index():
        return {'success': True}
    
    
    @app.get('/important_api')
    @rate.rate_limited(lambda request: request.client.host)
    def query_important_data(request: Request):
        data = 'important data'
        return {'success': True, 'data': data}
    
    
    if __name__ == "__main__":
        uvicorn.run("code1228:app", debug=True, reload=True)
    
    
    1. run this python file.

    2. test the link.http://127.0.0.1:8000/important_api

    Login or Signup to reply.
  4. You can use https://github.com/abersheeran/asgi-ratelimit

    Compared to https://pypi.org/project/fastapi-limiter/ and https://pypi.org/project/slowapi/, it can better meet your needs.

    This is a example: after exceeding the five times per second access limit, block a specific user for 60 seconds.

    app.add_middleware(
        RateLimitMiddleware,
        authenticate=AUTH_FUNCTION,
        backend=RedisBackend(),
        config={
            r"^/user": [Rule(second=5, block_time=60)],
        },
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search