AI Transport pricing
How AI Transport operations contribute to your message count and strategies to optimize costs.
The AI Transport SDK is built on top of Pub/Sub. All AI Transport operations generate Pub/Sub messages that follow the same counting rules as Pub/Sub, and Ably counts messages rather than tokens.
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.
AI Transport operations
The following table shows how AI Transport operations contribute to your message count:
| Operation | Messages counted |
|---|---|
| Send an event | |
| Send a message | 1 inbound message per message the codec encodes the event to |
| Delivery | 1 outbound message per subscriber |
| Streamed response | |
| Open a streamed message | 1 inbound message |
| Append | 1 inbound message per published append after rollup |
| End a stream | 1 inbound message |
| Other events in the response | 1 inbound message per event |
| Delivery | 1 outbound message per subscriber for each published message and append |
| History and replay | |
| History retrieval | 1 outbound message per retrieved message |
| Agent presence | |
| Presence enter | 1 inbound message |
| Presence leave | 1 inbound message |
| Presence update | 1 inbound message |
| Presence event delivery | 1 outbound message per presence 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 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
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.
Channel time contributes to your channel minutes. A channel becomes inactive approximately one minute after the last subscriber detached or the last message was published. The billable channel time for a conversation therefore tracks how long a client or an agent stays attached rather than how many messages pass through it.
Connections
Ably bills each connected client for connection minutes. A connection-minute is counted for every minute a client maintains an open connection, regardless of activity. Clients that remain connected but idle still accrue connection minutes.
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.
Cost optimization
Tune the append rollup window
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.
Only enable the rule for channels that require it
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.
Load long conversations from your own store
Reading history from the channel counts one outbound message per retrieved message. Serve the stored part of a conversation from your own database, and replay the channel only from the serial your store holds, so the replay retrieves only what your store does not have.
Close transports and connections when the conversation stops
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.
Read next
- Ably pricing: the current message, channel, and connection rates. Contact Ably for a quote.
- AI chatbot pricing example: a worked monthly cost for a chatbot handling 35,000 conversations.
- Going to production: the limits, retention, and monitoring to settle alongside cost.