Async Best Practices
Rules for productive asyncio in FastAPI-era services - and when to stay sync.
Search across all documentation pages
Rules for productive asyncio in FastAPI-era services - and when to stay sync.
asyncio.run per request.time.sleep, sync requests, sync DB in coroutines.to_thread. Cap concurrency with semaphore.return_exceptions=True when partial success is valid. APIs returning per-item errors.No - only when concurrent I/O waits dominate. CPU-bound or low concurrency may be slower due to complexity.
Runs in threadpool - OK for rare blocking libs; async def + native await scales better.
If most route time is in threads, migrate driver or deploy sync workers instead.
Strong async use cases - long-lived connections on one loop.
asyncio.run once at main - fine; keep internals async if using async HTTP.
Rare - one loop per thread; do not share clients across loops.
Async does not parallelize CPU - offload compute separately.
Use async-friendly handlers or queue to thread listener - avoid blocking emit in hot path.
Isolate in thread pool, cache results, or replace vendor - document latency cost.
Moderate blocking I/O, few endpoints, team unfamiliar with async debugging.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026