AI Transport pricing

AI Transport is priced on the number of unique users that talk to your agents each month. Agents, tokens, streamed responses and connection time don't affect your bill.

Ably prices on monthly active users (MAUs). A user who interacts with your application counts once in a calendar month, whichever products they use, however many conversations they have, however many tokens your agents stream to them, and however long they stay connected. Agents are never counted.

Monthly active users

An MAU is a unique clientId that is active during a calendar month. The AI Transport SDK runs a transport over a Pub/Sub realtime client, and a clientId becomes active when a transport is created over a realtime client from the device package that connects to Ably. It is counted once for that month, whether the user had one conversation or a hundred.

Each clientId is counted once per app. The same user counts once whether they connect from one device or several, and whether they use AI Transport alone or AI Transport alongside Pub/Sub, Chat, Spaces, LiveObjects or LiveSync in the same app.

Identifying users

Because an MAU is identified by their clientId, the way you assign them decides what you are billed for:

  • Assign one clientId per user, and use it on every device and in every session. A user who talks to your agent from a phone and a laptop with different clientIds counts as two MAUs.
  • Never share a clientId between users.

Anonymous users

Users who are anonymous to your application still require a clientId. Generate an identifier for each anonymous user and store it on their device, so that a returning anonymous user counts only once. If your application generates a fresh identifier on every visit, each visit counts as a new MAU.

Agents and servers

An agent runs its transport over a realtime client created from the server package, so agents are not counted as MAUs, however many conversations they handle, responses they stream or connections they hold. The same applies to any other realtime or HTTP client created from the server package. On token authentication, the token must also carry the x-ably-clientType claim with the value server.

If you call the REST API directly rather than through an SDK, declare the caller as a server. With API key authentication, add clientType=server as a request or connection parameter. With token authentication, add the x-ably-clientType claim with the value server to the JWT you issue to the server. A request that doesn't declare itself as a server is counted as a user.

Resource-based pricing

If your account is still on resource-based pricing, AI Transport is charged on what you use rather than on your number of MAUs: per million messages, per million channel minutes and per million connection minutes. Ably counts messages rather than tokens. How messages are counted lists which AI Transport operations generate messages.

To move your account to MAU pricing, follow the migration guide.

Messages

Every message counts once when it is published, then once more for each subscriber that receives it. On a conversation's channel the subscribers are every subscribed user client, every subscribed agent, and the history store, because AI Transport stores every message. A publisher receives its own message back too, because the transport's echoMessages option defaults to true, so a client that sends a message is one of its subscribers.

A message published on a channel with one browser client and one subscribed agent therefore counts four times: once to publish it, once for the client, once for the agent, and once to store it. An agent that pipes a response without subscribing does not receive it, so it does not count as a subscriber.

The codec decides how many messages each event becomes. The Vercel AI SDK codec sends a user message as one message, whatever parts it holds. In a response, each text, reasoning and tool-input stream is one message that grows by appends, and every other chunk is its own message. A plain text response with the Vercel AI SDK codec publishes start, start-step, text-start, the text stream's message and its appends, text-end, finish-step and finish. The wire protocol describes how each message is written.

The OpenAI Responses codec publishes nothing for response.created, response.in_progress and response.queued, and one message for each other event that is not part of a stream.

The AG-UI codec does not publish MESSAGES_SNAPSHOT, and publishes each *_CHUNK event as its own message. Text, tool call arguments and reasoning each stream as appends to one message per stream.

Individual appends each count as one message, but Ably stores and serves a streamed response as one aggregated message, so retrieving it from history counts as one outbound message rather than one per append.

Append rollup

An agent appends each delta to the streamed message as the model produces it, and Ably combines the appends that fall inside the rollup window into a single published message. Each published message is what gets counted, inbound and then outbound to every subscriber, so the rollup window sets the ceiling on the message count for a stream.

The default window of 40ms caps a single response at 25 published messages per second, or the model's output rate if that is lower. Increasing the window length lowers the number of messages per second: at 100ms a stream publishes at most 10 messages per second. Clients receive the same content at any window length, delivered in fewer and larger updates. The rollup configuration table provides more information on setting the appendRollupWindow value.

Channels and connections

Each conversation uses a single Ably channel. Everything in the conversation shares that channel: the user's messages, streamed responses, presence, and LiveObjects state. Two responses in flight at once share the same channel rather than opening more. A channel becomes inactive approximately one minute after the last subscriber detached or the last message was published, so the channel minutes for a conversation track how long a client or an agent stays attached rather than how many messages pass through it.

An agent holds a connection too. Closing a transport detaches its channel but leaves the Ably client connected, so connection minutes stop only when the application closes the client or the process exits. A serverless agent that exits after each response accrues connection minutes only while it runs. A long-lived agent process accrues them continuously.

Message persistence

AI Transport requires the Message annotations, updates, deletes, and appends rule on the channels your conversations use, because appends only work on channels that have this rule enabled. The rule also stores every message on those channels, which is why each message counts once for the history store.

This applies to every channel the rule's match expression selects, not only to the channels that carry conversations.

Resource efficiency

Regardless of whether you are on MAU pricing or resource-based pricing, effectively managing resources is beneficial to your application:

  • Raise appendRollupWindow above the 40ms default on the connection that publishes the stream. This is the largest single lever on the message count of a streamed response, because it caps how many appends Ably publishes per second. Trade it against how granular you want delivery to feel.
  • Enable the rule on a match expression that selects conversations only, such as conversations:*, rather than on a broad one. The rule stores every message on every channel it matches, so a narrow expression keeps unrelated channels out of your stored message count.
  • Serve the stored part of a conversation from your own database, and replay the channel only from the serial your store holds, so that the replay retrieves only what your store does not have. Reading history from the channel counts one outbound message per retrieved message.
  • On the client, call close() on the transport, or unmount its TransportProvider, when the user navigates away. On the agent, call close() when the response completes. Closing a transport detaches the channel it attached, so the channel can become inactive. Closing the Ably client is what stops connection minutes, on both sides.