aiobotocore is boto3 with await in front, and its compatibility work is the product
asyncio support for botocore library using aiohttp
At a glance
- What is it?
- The async AWS client for Python wraps botocore rather than replacing it, which means the interesting engineering is in tracking botocore's service model, keeping two HTTP backends, and pinning a narrow botocore range. The httpx backend being deprecated in favour of a fork is the detail that tells you how quickly the transport layer moves underneath you.
- Who is it for?
- Adopt aiobotocore when your Python service is already async and you need concurrent AWS calls, since the alternative is either blocking the event loop with boto3 or writing a second AWS client yourself, and the readme's own argument is that most services work by putting await in front of a boto3 client method.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It wraps botocore, which is why the version range is so narrow
The description calls this a mostly full featured asynchronous version of botocore, and that word mostly carries the whole design. aiobotocore is not a separate AWS client with its own service definitions. It reuses botocore's service model, its serialisation and its credential resolution, and replaces the transport and the call layer. The consequence appears immediately in the dependency declaration: botocore is pinned to a range that spans a narrow window rather than a permissive floor, and it is a low ceiling as well as a low floor. Every other dependency is similarly bounded, from aiohttp to multidict, wrapt and jmespath, and typing-extensions is conditioned on the interpreter version. That style of pinning is a deliberate choice for a project whose value depends on tracking an upstream library closely: if aiobotocore fell behind botocore, its users would get stale service models and new operations would simply not exist, so the release process is essentially a re-validation against a new botocore. For a consumer it means the upgrade is a two-variable problem, and the practical discipline is to let the resolver pick aiobotocore and let it choose a compatible botocore, rather than pinning botocore in your own requirements and discovering the conflict at install time.
Two HTTP backends, and one of them is already deprecated
The transport is pluggable, and the optional dependency groups show how that has played out. The default path uses aiohttp, which is a required dependency with a bounded range. Then there is an extra for the httpx backend, recommended one, which pulls anyio plus httpx2, and the comment in the project file is explicit that httpx2 is the maintained fork of httpx. Alongside it sits a deprecated httpx extra that installs the legacy httpx package, with a comment saying that extra now prefers httpx2 and that the legacy package emits a deprecation warning when used as a backend. That is an unusual amount of churn for a transport layer and it tells you two things. First, aiohttp is the conservative choice, and httpx is where the project expects to end up. Second, the fork situation is not theoretical: if your project already depends on httpx, installing this extra can pull a second package with a similar name and a different lineage, and that is a dependency-graph decision to make deliberately rather than by accepting the default. The readme does not document how to select a backend at runtime, only how to install one, so assume the choice is made by which extra you install and confirm that against the documentation before you rely on it.
Clients are context managers, and the body stream is the part people get wrong
The basic example in the readme is the shape most code will follow, and the whole client lifecycle is a context manager:
async with session.create_client('s3', region_name='us-west-2',
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
aws_access_key_id=AWS_ACCESS_KEY_ID) as client:
resp = await client.put_object(Bucket=bucket,
Key=key,
Body=data)The session is obtained from the module's session factory, and the client is closed for you when the block exits. Credentials can be passed to the client factory directly, and a region is named as a keyword argument. The calls are then awaited: an upload, a request for the object's access control list, a fetch, a delete. Two details in that example are the real documentation. First, the fetched object is a response whose body is entered as an async context manager before reading, with a comment saying this ensures the connection is correctly reused or closed. That is the part that distinguishes a correct async client from one that leaks sockets, and it means you must close the body stream or hold connections until the pool is exhausted. Second, pagination is a separate awaitable: you ask the client for a paginator, then iterate it asynchronously over the results. A paginator that is not consumed with an async loop will not behave like the synchronous one, and the readme's example nests a plain loop over each page's contents, which is the correct combination. The library therefore has three lifecycle objects to manage, session, client and body stream, and treating them as one is the most common source of trouble.
AsyncExitStack is how you compose clients, and the readme shows both patterns
The context manager section of the readme is unusually practical, and it exists because a client has to be closed and that is awkward inside larger code. The first pattern wraps the client in a class that holds an AsyncExitStack, enters the client as an async context in the class's own enter method, and unwinds the stack in the exit method. The second pattern is a function that takes a session and an external stack, enters the client into that stack and returns it, so the caller owns the lifetime. Both are shown, and the point of the second is that a caller who already has a stack can add a client to it without a wrapper class, which is the composition-friendly form. The exit method's signature in the example takes the exception triple and forwards it, so exceptions propagate rather than being swallowed, which is the behaviour you want. Read these as a template rather than as documentation, because the readme does not discuss error handling, retries or timeouts in this section at all. Those are handled by botocore's configuration underneath, and they are configured through the same mechanism boto3 uses, so knowledge of boto3 configuration transfers directly. The gap is documentation of where the async boundary sits on a timeout, which is a question to answer in your own testing.
Service coverage is uneven, and the readme admits which services are which
The supported services table is the most honest part of the documentation and deserves to be quoted rather than paraphrased. It is described as a non-exhaustive list of what the test suite runs against. S3 is marked working. DynamoDB, SNS, SQS and Kinesis are marked as having basic methods tested. CloudFormation is marked as having stack creation tested. The readme then gives the reason the list is short, which is that because of the way boto3 is implemented it is highly likely that even for services not listed you can take a boto3 client for that service and put await in front of its methods, and it gives a named example of awaiting a method that lists Athena named queries. Then it invites issues for services people want tested. This is a good-faith statement of coverage, and it tells you how to think about risk. S3, the service with the most complex body and pagination semantics, is the one with the deepest testing, which is the correct place to have spent the effort. A service marked basic methods tested may have its create and read paths covered and its long tail untested, and the failure mode of an untested method in an async wrapper is usually obvious, since an unawaited call returns a coroutine rather than a result. Still, that is a failure you would rather meet in a test than in production.
Type annotations are a separate package with two shapes
The readme has a section on enabling type checking and code completion, and it points at a separate types package rather than shipping stubs in the main distribution. The reason is a packaging one: a full set of annotations for every supported botocore service is large, and the main package stays small by leaving them out. Two variants are offered. The full version, installed with an extra, covers a set of commonly used services and provides the overloads for the client factory so that a service name in code is resolved to a client with the right method signatures. The lite version is more frugal on memory and explicitly does not provide those client factory overloads, which means you must annotate the client yourself. That trade is worth understanding: without the overloads, a client variable is an opaque handle and a typo in a method name is a type error only if you have declared a type for the client. If your editor is where you catch most mistakes, the full variant is the one to install, and if you are running a large development machine or a constrained CI image, the lite variant plus explicit annotations is a deliberate trade. Both are installed with the normal package installer, and the readme shows the essential and per-service forms.
Repository shape, release cadence, and the licensing position
The project is run with modern Python tooling and that shows in the layout. The build backend is hatchling with a plugin that reads the readme for the package long description, and the project metadata requires Python 3.10 or later and classifies itself as a beta-stage development status across interpreters from 3.10 to 3.14. A lock file is checked in, and there is a pre-commit configuration, a coverage configuration with a badge, a readthedocs configuration, a changelog, a contributing guide, and a Git blame ignore file that exists because of large automated reformats. The documentation directory and a plugins directory sit alongside the package, an examples directory, a scripts directory, and a tests directory. Two entries are unusual for a library repository and both point at the direction of the project: a directory named for a coding assistant configuration at the top level, and a Claude plugin manifest. The release cadence visible in the recent versions is roughly fortnightly at the time of writing, with 3.8.0, 3.9.0 and 3.9.1 in a period of about five weeks, which is consistent with a project that re-releases against new botocore versions. The licence is Apache-2.0, which carries an explicit patent grant and is a permissive choice for infrastructure code.
Editorial conclusion
Adopt aiobotocore when your Python service is already async and you need concurrent AWS calls, since the alternative is either blocking the event loop with boto3 or writing a second AWS client yourself, and the readme's own argument is that most services work by putting await in front of a boto3 client method. Do not adopt it as a drop-in with no attention to versions, because the project pins botocore to a narrow range and a botocore upgrade outside that range is the failure mode you will hit first. Four things to verify. That your botocore version falls inside the declared range, since it is a narrow window rather than a floor. Which HTTP backend you install, because the httpx extra is deprecated in favour of a fork package and installing the legacy one emits a deprecation warning. Where your type checking comes from, because the readme says annotations ship as a separate types package and that the lite variant omits the client factory overloads. And which services you actually depend on, since the readme's own coverage table lists only S3 as fully working and marks the rest as basic methods tested, with CloudFormation limited to stack creation. The licence is Apache-2.0, version 3.9.1 was released on 2026-08-22, and the last push was on 2026-09-18.
Frequently asked questions
How do I install aiobotocore?
Run pip install aiobotocore. The project requires Python 3.10 or later, and it depends on aiohttp for the default transport, with an extra available for the httpx backend.
Which AWS services does aiobotocore support well?
The readme's table marks S3 as working, DynamoDB, SNS, SQS and Kinesis as having basic methods tested, and CloudFormation as having stack creation tested. It also says that because of how boto3 is implemented, you can generally take a boto3 client for any service and put await in front of its methods.
How do I get type checking for aiobotocore?
Install the separate types package, either the full variant with an extra for common services or the lite variant which is more RAM-friendly but does not provide session.create_client overloads and needs explicit annotations. The main package does not ship the annotations.
What is the relationship between aiobotocore and botocore versions?
aiobotocore wraps botocore rather than replacing it, and the project manifest pins botocore to a narrow range with both a floor and a ceiling. Releases follow botocore closely, so a botocore version outside that range is the dependency conflict to expect.
What licence is aiobotocore released under?
Apache-2.0, with the licence file declared in the project metadata. The recent releases include 3.9.1 on 2026-08-22, and the last push to the main branch was on 2026-09-18.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/aio-libs-aiobotocore)