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:

OperationMessages counted
Send an event
Send a message1 inbound message per message the codec encodes the event to
Delivery1 outbound message per subscriber
Streamed response
Open a streamed message1 inbound message
Append1 inbound message per published append after rollup
End a stream1 inbound message
Other events in the response1 inbound message per event
Delivery1 outbound message per subscriber for each published message and append
History and replay
History retrieval1 outbound message per retrieved message
Agent presence
Presence enter1 inbound message
Presence leave1 inbound message
Presence update1 inbound message
Presence event delivery1 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.