How to query aws resources using boto3 and asyncio? Is this possible?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
boto3 is synchronous by default — every API call blocks the calling thread until the response arrives. Python's asyncio provides cooperative concurrency for I/O-bound tasks, but boto3 does not natively support async/await. To use boto3 with asyncio, you have three options: run synchronous boto3 calls in a thread pool via asyncio.to_thread(), use the third-party aiobotocore library (which provides true async AWS API calls), or use aioboto3 (a higher-level async wrapper). The thread pool approach works with standard boto3 and requires no additional dependencies.
Method 1: asyncio.to_thread() (Python 3.9+)
Run synchronous boto3 calls in the default thread pool to avoid blocking the event loop.
For Python 3.7-3.8, use loop.run_in_executor() instead:
Method 2: aiobotocore (True Async)
aiobotocore is a thin async wrapper around botocore (the library underlying boto3) that provides native async/await support without threads.
Method 3: aioboto3 (High-Level Async)
aioboto3 wraps aiobotocore to provide an interface similar to boto3, including resource-level abstractions.
Paginating Async Results
Many AWS APIs return paginated results. Handle pagination in async code:
Common Pitfalls
- Creating
boto3clients inside the event loop without threading:boto3.client()andboto3.resource()are synchronous and may perform network calls (e.g., STS for credentials). Creating them inside an async function withoutto_thread()blocks the event loop. Create clients in a thread or before starting the event loop. - Sharing a single
boto3client across tasks:boto3clients are not thread-safe. When usingasyncio.to_thread()withasyncio.gather(), each task should create its own client, or use a thread-local client pattern. - Mixing
aiobotocoreandboto3in the same project:aiobotocorepins a specific version ofbotocorewhich may conflict with the versionboto3requires. This causes version conflicts duringpip install. Choose one approach per project. - Not using
async withforaiobotocoreclients:aiobotocoreclients must be used withinasync withblocks to properly close HTTP connections. Forgetting the context manager leaks connections and eventually causesConnectionErroror resource exhaustion. - Rate limiting not handled: Running many AWS API calls concurrently with
asyncio.gather()can trigger AWS throttling. Useasyncio.Semaphoreto limit concurrency:sem = asyncio.Semaphore(10)andasync with sem: await api_call().
Summary
boto3is synchronous — useasyncio.to_thread()to run it concurrently without blocking the event loop- Use
aiobotocorefor true async AWS API calls without threads - Use
aioboto3for a higher-level async interface similar toboto3 - Always use
async withcontext managers foraiobotocore/aioboto3clients - Limit concurrent API calls with
asyncio.Semaphoreto avoid AWS throttling

