Overview

The Python SDK ships an async client alongside the synchronous one. Reach for it when your backend already runs on an async stack (FastAPI, aiohttp, or anything built on asyncio), or when you need to issue many Stream calls concurrently without tying up a thread per request. The async client mirrors the synchronous client: every network call becomes a coroutine you await.

Initializing the async client

Import AsyncStream and construct it exactly like the synchronous Stream client:

from getstream import AsyncStream

client = AsyncStream(api_key="your-api-key", api_secret="your-api-secret")

It also reads STREAM_API_KEY and STREAM_API_SECRET from environment variables, so you can construct it with no arguments:

client = AsyncStream()

If you already have a synchronous Stream instance, convert it with as_async():

from getstream import Stream

client = Stream(api_key="your-api-key", api_secret="your-api-secret").as_async()

Making async calls

The API surface is identical to the synchronous client, except each network call is now awaited. Here is the getting-started flow rewritten for async:

import asyncio

from getstream import AsyncStream
from getstream.models import UserRequest, ChannelInput, MessageRequest


async def main():
    async with AsyncStream(api_key="your-api-key", api_secret="your-api-secret") as client:
        # Upsert a user
        await client.upsert_users(UserRequest(id="john", name="John"))

        # Create or join a channel
        channel = client.chat.channel("messaging", "hello-world")
        await channel.get_or_create(data=ChannelInput(created_by_id="john"))

        # Send a message
        await channel.send_message(
            MessageRequest(text="Hello, Stream!", user_id="john")
        )


asyncio.run(main())

Note that channel(...) just builds a channel object locally, so it is not awaited. Token creation is also a local operation with no network call, so keep it synchronous:

token = client.create_token(user_id="john")

Cleaning up

The async client keeps an open connection pool, so close it when you are done. The async with block above does this for you: on exit it closes the pool along with any chat, video, or moderation sub-clients. If you manage the client's lifetime yourself, call await client.aclose() explicitly:

client = AsyncStream(api_key="your-api-key", api_secret="your-api-secret")
try:
    await client.upsert_users(UserRequest(id="john", name="John"))
finally:
    await client.aclose()

Reuse a single client for the lifetime of your process rather than creating one per request. It maintains one shared connection pool across chat, video, and moderation. See Connection Pooling for tuning the pool.

Running calls concurrently

The payoff of async is concurrency. Use asyncio.gather to run independent calls at the same time instead of awaiting them one after another:

await asyncio.gather(
    client.upsert_users(UserRequest(id="john", name="John")),
    client.upsert_users(UserRequest(id="jane", name="Jane")),
)

The client caps concurrent connections per host, so a large fan-out is queued through the pool rather than opening an unbounded number of sockets.

Things to know

  • Token-only clients cannot mint tokens or call admin endpoints. Pass api_secret for a server-side client that has full API access. See Tokens & Authentication.
  • The async client does not support Feeds. Accessing client.feeds raises NotImplementedError; use the synchronous Stream client for Feeds.