All Articles
Deep Diveยท5 min read

Why Your FastAPI App Slows Down Under Concurrency

Your FastAPI endpoint is fast in isolation but grinds to a halt under load. The usual cause is one blocking call sitting inside an async function.

Part of: Python Programming & Development

Your FastAPI endpoint responds in 20ms when you test it alone. Then real traffic arrives and latency climbs to seconds. Nothing in your code got slower. So what happened?

The Symptom

You have an async def endpoint that calls a library like requests or a synchronous database driver:

import requests @app.get("/report") async def report(): data = requests.get("https://api.example.com/data").json() # blocking! return {"data": data}

Under one user this is fine. Under fifty concurrent users, every request queues behind every other.

Why It Happens

FastAPI runs async def endpoints on a single event loop. The loop can juggle thousands of tasks โ€” but only if each task yields while it waits. requests.get() never yields. It blocks the entire event loop until the network call returns. While it blocks, no other request can make progress.

You put a synchronous, blocking call inside a cooperative, single-threaded loop. Concurrency collapses to one-at-a-time.

The Fix

Option A โ€” use an async client. Replace blocking libraries with async ones so the call yields control while waiting.

import httpx @app.get("/report") async def report(): async with httpx.AsyncClient() as client: resp = await client.get("https://api.example.com/data") return {"data": resp.json()}

Now the event loop is free to serve other requests during the await.

Option B โ€” make the endpoint synchronous. If you must call blocking code, define the endpoint with plain def. FastAPI then runs it in a thread pool, keeping the event loop unblocked.

@app.get("/report") def report(): # note: def, not async def data = requests.get("https://api.example.com/data").json() return {"data": data}

The rule: async def endpoints must only await non-blocking calls. Anything blocking belongs in a def endpoint or a thread executor.

Prevention

  • โ–ธAudit every async def for synchronous I/O: file reads, time.sleep, sync DB drivers, requests.
  • โ–ธUse asyncio.sleep instead of time.sleep inside async code.
  • โ–ธLoad-test with concurrency, not a single request โ€” the problem is invisible otherwise.

The takeaway: async is not automatically faster. A single blocking call inside the event loop serializes your whole app. Match blocking work to threads and async work to await.

#Python#FastAPI#async#Performance

Want to go deeper?

Python Programming & Development

View Course