diff --git a/src/data/nav/aitransport.ts b/src/data/nav/aitransport.ts index 197c20cc4f..383b41ffd7 100644 --- a/src/data/nav/aitransport.ts +++ b/src/data/nav/aitransport.ts @@ -192,6 +192,10 @@ export default { }, ], }, + { + name: 'AI Transport pricing', + link: '/docs/ai-transport/pricing', + }, ], api: [], } satisfies NavProduct; diff --git a/src/data/nav/chat.ts b/src/data/nav/chat.ts index 406ec76c8c..34bd92c6d5 100644 --- a/src/data/nav/chat.ts +++ b/src/data/nav/chat.ts @@ -223,6 +223,10 @@ export default { }, ], }, + { + name: 'Chat pricing', + link: '/docs/chat/pricing', + }, { name: 'Guides', pages: [ diff --git a/src/data/nav/liveobjects.ts b/src/data/nav/liveobjects.ts index 87732773d5..8c7e6241b2 100644 --- a/src/data/nav/liveobjects.ts +++ b/src/data/nav/liveobjects.ts @@ -55,11 +55,6 @@ export default { link: '/docs/liveobjects/concepts/synchronization', languages: ['javascript', 'swift', 'java'], }, - { - name: 'Billing', - link: '/docs/liveobjects/concepts/billing', - languages: ['javascript', 'swift', 'java'], - }, ], }, { @@ -108,6 +103,10 @@ export default { }, ], }, + { + name: 'LiveObjects pricing', + link: '/docs/liveobjects/pricing', + }, ], api: [ { diff --git a/src/data/nav/livesync.ts b/src/data/nav/livesync.ts index 4c1e3230ff..d6300f86ab 100644 --- a/src/data/nav/livesync.ts +++ b/src/data/nav/livesync.ts @@ -43,6 +43,10 @@ export default { }, ], }, + { + name: 'LiveSync pricing', + link: '/docs/livesync/pricing', + }, ], api: [ { diff --git a/src/data/nav/platform.ts b/src/data/nav/platform.ts index 8e1df1417d..a5070715b1 100644 --- a/src/data/nav/platform.ts +++ b/src/data/nav/platform.ts @@ -125,6 +125,10 @@ export default { link: '/docs/platform/pricing/billing', name: 'Billing', }, + { + link: '/docs/platform/pricing/message-counting', + name: 'Message counting', + }, { link: '/docs/platform/pricing/limits', name: 'Limits', diff --git a/src/data/nav/pubsub.ts b/src/data/nav/pubsub.ts index 6936a8f9ab..ecf3d63a1f 100644 --- a/src/data/nav/pubsub.ts +++ b/src/data/nav/pubsub.ts @@ -356,6 +356,10 @@ export default { }, ], }, + { + name: 'Pub/Sub pricing', + link: '/docs/pub-sub/pricing', + }, { name: 'Guides', pages: [ diff --git a/src/data/nav/spaces.ts b/src/data/nav/spaces.ts index e230c99349..811b2054fb 100644 --- a/src/data/nav/spaces.ts +++ b/src/data/nav/spaces.ts @@ -57,6 +57,10 @@ export default { }, ], }, + { + name: 'Spaces pricing', + link: '/docs/spaces/pricing', + }, ], api: [ { diff --git a/src/pages/docs/ai-transport/pricing.mdx b/src/pages/docs/ai-transport/pricing.mdx new file mode 100644 index 0000000000..5414b98ac6 --- /dev/null +++ b/src/pages/docs/ai-transport/pricing.mdx @@ -0,0 +1,39 @@ +--- +title: "AI Transport pricing" +meta_description: "Understand how AI Transport token streaming contributes to your message count, including per-token and per-response patterns." +meta_keywords: "AI Transport pricing, message counting, token streaming, message per response, message per token" +intro: "How AI Transport operations contribute to your message count and strategies to optimize costs." +--- + +[AI Transport](/docs/ai-transport) uses standard Pub/Sub messaging. Each token or message published to Ably counts as an inbound message, and each delivery to a subscriber counts as an outbound message. + +## AI Transport operations + +The following table shows how AI Transport operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| Token publish (per-token) | 1 inbound message per token | +| Token publish (per-response with [append rollup](/docs/ai-transport/token-streaming/token-rate-limits#per-response)) | Multiple tokens conflated into fewer inbound messages | +| Token delivery | 1 outbound message per subscriber | +| [Persistence](/docs/storage-history/storage) storage | 1 additional message per stored message | + +The total cost depends on the [token streaming pattern](/docs/ai-transport/token-streaming#token-streaming-patterns) you choose. With per-token streaming, each token is a separate inbound message. With [message-per-response](/docs/ai-transport/token-streaming/message-per-response), append rollup conflates multiple tokens into fewer inbound messages, reducing costs. For example, 300 tokens can be conflated to approximately 100 inbound messages. + + + +## Channels and connections + +Each channel used for token streaming contributes to your [channel count](/docs/platform/pricing#channels). Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections) per minute of connection time. + +## Cost optimization + +### Use message-per-response streaming + +The [message-per-response](/docs/ai-transport/token-streaming/message-per-response) pattern with [append rollup](/docs/ai-transport/token-streaming/token-rate-limits#per-response) conflates multiple tokens into fewer inbound messages, significantly reducing costs compared to per-token streaming. + +### Use ephemeral messages + +Mark messages as [ephemeral](/docs/pub-sub/advanced#ephemeral) to exempt them from persistence, rewind, resume, and integrations. Use this for streaming tokens where history is not needed. diff --git a/src/pages/docs/chat/pricing.mdx b/src/pages/docs/chat/pricing.mdx new file mode 100644 index 0000000000..4980618dff --- /dev/null +++ b/src/pages/docs/chat/pricing.mdx @@ -0,0 +1,72 @@ +--- +title: "Chat pricing" +meta_description: "Understand how Chat SDK features contribute to your message count, including messages, typing indicators, reactions, and cost optimization strategies." +meta_keywords: "chat pricing, message counting, typing indicators, reactions, cost optimization" +intro: "How Chat SDK features contribute to your message count and strategies to optimize costs." +--- + +The [Chat SDK](/docs/chat) is built on top of [Pub/Sub](/docs/pub-sub). All Chat operations generate Pub/Sub messages that follow the same counting rules. + +## Chat operations + +The following table shows how Chat operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| [Messages](/docs/chat/rooms/messages) || +| Send message | 1 inbound message | +| Message delivery | 1 outbound message per subscriber | +| Update message | 1 inbound message | +| Message update delivery | 1 outbound message per subscriber | +| Delete message | 1 inbound message | +| Message deletion delivery | 1 outbound message per subscriber | +| [History](/docs/chat/rooms/history) retrieval | 1 message per retrieved message | +| [Typing indicators](/docs/chat/rooms/typing) || +| Typing keystroke | 1 inbound message; repeats every heartbeat interval (default 10s) | +| Typing indicator delivery | 1 outbound message per subscriber per heartbeat | +| Typing stop | 1 inbound message | +| Typing stop delivery | 1 outbound message per subscriber | +| [Reactions](/docs/chat/rooms/reactions) || +| Room reaction | 1 inbound message | +| Room reaction delivery | 1 outbound message per subscriber | +| [Message reaction](/docs/chat/rooms/message-reactions) send | 1 inbound message | +| [Message reaction](/docs/chat/rooms/message-reactions) delete | 1 inbound message | +| Message reaction summary delivery | 1 outbound message per subscriber; multiple reactions may be rolled up into a single summary | +| [Presence](/docs/chat/rooms/presence) and [occupancy](/docs/chat/rooms/occupancy) || +| Presence enter | 1 inbound message | +| Presence leave | 1 inbound message | +| Presence update | 1 inbound message | +| Presence event delivery | 1 outbound message per presence subscriber | +| Occupancy event | 1 outbound message per subscriber (generated on membership changes, debounced up to 15s) | +| [Moderation](/docs/chat/moderation) || +| Moderation action | 1 inbound message; triggers a message update or delete which follows standard delivery | + + + +## Rooms, channels, and connections + +Each Chat room maps to a set of underlying Pub/Sub channels. Each room feature (messages, typing, reactions, presence, occupancy) uses its own dedicated channel. The total number of channels used by a room depends on which features are active. + +Channels contribute to your [channel count](/docs/platform/pricing#channels). The more rooms a client is attached to, and the more features enabled per room, the more channels are consumed. + +Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections). Ably counts a connection-minute for every minute a client maintains an open connection, regardless of activity. Clients that remain connected but idle still accrue connection minutes. + +## Cost optimization + +### Reduce typing indicator frequency + +Increase the `heartbeatThrottleMs` [room option](/docs/chat/rooms#typing) to reduce typing indicator event frequency. The default is 10 seconds. Increase this value in rooms that tolerate delayed typing feedback. + +### Use server-side batching + +[Server-side batching](/docs/messages/batch#server-side) groups messages into single deliveries. Use this for high-throughput rooms where slight delay is acceptable. + +### Detach from unused rooms + +[Detach](/docs/chat/rooms#detach) from rooms when they are no longer needed, and [release](/docs/chat/rooms#release) rooms that are no longer in use. This reduces your channel count and connection usage. Close connections when the client is done to stop accruing connection minutes. + +### Use occupancy instead of presence + +Use [occupancy](/docs/chat/rooms/occupancy) instead of [presence](/docs/chat/rooms/presence) when you only need member counts, not individual identities. This avoids the n-squared presence event fan-out. For example, 200 members joining and leaving generates approximately **80,400 messages**. See [large-scale presence sets](/docs/presence-occupancy/presence#large-presence) for details. diff --git a/src/pages/docs/liveobjects/concepts/billing.mdx b/src/pages/docs/liveobjects/concepts/billing.mdx deleted file mode 100644 index a35bdb0126..0000000000 --- a/src/pages/docs/liveobjects/concepts/billing.mdx +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Billing -meta_description: "Understand how LiveObjects operations contribute to your Ably usage and billing." ---- - - - - - - - - - - - -LiveObjects operations are billed as messages. This page explains how different LiveObjects operations contribute to your Ably usage. - - - -## Message counting - -Each operation is sent as an [`ObjectMessage`](/docs/liveobjects/concepts/operations#properties). Ably bills for each outbound message sent from a client, and for each inbound message delivered to each client. - -When a client performs an operation (such as setting a value on a `LiveMap` or incrementing a `LiveCounter`), this generates an outbound message. When that operation is broadcast to other connected clients, each client receives an inbound message. - -For example, if 5 clients are connected to a channel and one client increments a counter: - -- 1 outbound message (from the client performing the increment) -- 5 inbound messages (one delivered to each of the 5 connected clients, including the client that sent it) - - - -## LiveMap operations - -Removing a key and setting a primitive value always sends one message: - - -```javascript -// One message -await myObject.get('settings').set('theme', 'dark'); - -// One message -await myObject.get('settings').remove('theme'); -``` - - -Creating a shallow `LiveMap` sends two messages: one to create the new `LiveMap` object, and one to assign it to the target object: - - -```javascript -// Two messages: create map + assign to 'settings' -await myObject.set('settings', LiveMap.create({ - shallow: 'data' -})); -``` - - -Nested `LiveMap` or `LiveCounter` objects each generate their own creation messages: - - -```javascript -// Four messages: -// - create visits counter -// - create stats map -// - create settings map -// - assign to 'settings' -await myObject.set('settings', LiveMap.create({ - stats: LiveMap.create({ - visits: LiveCounter.create() - }) -})); -``` - - -Batch operations result in a message for each operation included in the batch: - - -```javascript -// Four messages -await myObject.get('settings').batch((ctx) => { - ctx.set('theme', 'dark'); - ctx.set('fontSize', 14); - ctx.set('notifications', true); - ctx.remove('oldSetting'); -}); -``` - - -## LiveCounter operations - -Incrementing and decrementing a counter always sends one message: - - -```javascript -// One message -await myObject.get('visits').increment(5); - -// One message -await myObject.get('visits').decrement(3); -``` - - -Creating a `LiveCounter` sends two messages: one to create the new `LiveCounter` object, and one to assign it to the target object: - - -```javascript -// Two messages: create counter + assign to 'visits' -await myObject.set('visits', LiveCounter.create(0)); -``` - - -Creating a counter nested within a `LiveMap` also generates messages for the parent map: - - -```javascript -// Four messages: -// - create visits counter -// - create stats map -// - create settings map -// - assign to 'settings' -await myObject.set('settings', LiveMap.create({ - stats: LiveMap.create({ - visits: LiveCounter.create(0) - }) -})); -``` - - -Batch operations result in a message for each operation included in the batch: - - -```javascript -// Three messages -await myObject.get('visits').batch((ctx) => { - ctx.increment(5); - ctx.increment(3); - ctx.decrement(2); -}); -``` - - - - -## Synchronization - -During initial synchronization and resynchronization, each object on the channel is sent as one or more messages. Large objects, such as maps with many entries, may be split across multiple messages. - -For example, if a channel contains 10 objects (such as `LiveMap` and `LiveCounter` instances), a client attaching to the channel will receive at least 10 messages during synchronization. - -Similarly, if a client becomes disconnected and needs to resynchronize, it will receive messages for each object that needs to be synchronized. - -Only [reachable](/docs/liveobjects/concepts/objects#reachability) objects are counted. Ably may send [tombstone](/docs/liveobjects/concepts/objects#tombstones) objects to the client, but these will not count towards your usage. - -## Subscriptions - -Subscribing to updates does not affect the number of messages received by a client. Any client attached to a channel with the `object-subscribe` capability automatically receives all object messages for that channel. Subscribing to updates on an object adds a listener that is called whenever the client receives updates for that object. - -## REST API - -The [LiveObjects REST API](/docs/liveobjects/rest-api-usage) also counts messages for operations performed. - -When fetching objects via the REST API, each instance of an [object type](/docs/liveobjects/concepts/objects#object-types) included in the response is counted as one message. - -Each operation published via the REST API counts as one message. When creating objects using the `path` field, the server constructs two messages (a create operation and a `MAP_SET` operation to assign it). diff --git a/src/pages/docs/liveobjects/index.mdx b/src/pages/docs/liveobjects/index.mdx index 16adb2e9b4..f4b01b99bf 100644 --- a/src/pages/docs/liveobjects/index.mdx +++ b/src/pages/docs/liveobjects/index.mdx @@ -94,7 +94,7 @@ LiveObjects [durably stores](/docs/liveobjects/storage) all objects on a channel LiveObjects usage is billed based on [Ably's standard pricing model](/docs/platform/pricing). Since LiveObjects is powered by Ably's channel messages, any interaction with LiveObjects - whether sending or receiving operations, maintaining active channels, or keeping connections open through the Realtime LiveObjects API - translates into billable usage. Storage of objects themselves does not incur additional costs; however, there is a [limit](/docs/liveobjects/storage) on the size of the channel object. -For details on how using LiveObjects contributes to your billable usage, see [Billing](/docs/liveobjects/concepts/billing). +For details on how using LiveObjects contributes to your billable usage, see [LiveObjects pricing](/docs/liveobjects/pricing). ### Realtime API diff --git a/src/pages/docs/liveobjects/pricing.mdx b/src/pages/docs/liveobjects/pricing.mdx new file mode 100644 index 0000000000..3963ad68a0 --- /dev/null +++ b/src/pages/docs/liveobjects/pricing.mdx @@ -0,0 +1,65 @@ +--- +title: "LiveObjects pricing" +meta_description: "Understand how LiveObjects operations contribute to your message count, including LiveMap, LiveCounter, synchronization, and REST API usage." +meta_keywords: "LiveObjects pricing, message counting, LiveMap, LiveCounter, ObjectMessages" +intro: "How LiveObjects operations contribute to your message count and strategies to optimize costs." +redirect_from: + - /docs/liveobjects/concepts/billing +--- + +Ably bills LiveObjects operations using ObjectMessages. An ObjectMessage is the unit of change in LiveObjects. Each state mutation, such as setting a key, incrementing a counter, or creating an object, generates one or more ObjectMessages. Each ObjectMessage follows the standard inbound/outbound counting pattern. + +## LiveObjects operations + +The following table shows how LiveObjects operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| [LiveMap](/docs/liveobjects/map) set or remove | 1 inbound message per operation | +| [LiveMap](/docs/liveobjects/map) create (shallow) | 2 inbound messages (create + assign) | +| [LiveCounter](/docs/liveobjects/counter) increment or decrement | 1 inbound message | +| [LiveCounter](/docs/liveobjects/counter) create | 2 inbound messages (create + assign) | +| ObjectMessage delivery | 1 outbound message per connected client | +| [Synchronization](#synchronization) | 1 message per object synchronized | +| [REST API](#rest-api) fetch | 1 message per object in response | +| [Batch](/docs/liveobjects/batch) operation | 1 message per operation in the batch | + +Nested object creation generates additional messages for each nested object. + + + +## Channels and connections + +Each LiveObjects channel contributes to your [channel count](/docs/platform/pricing#channels). Ably bills [connection minutes](/docs/platform/pricing#connections) per minute of connection time for each connected client. + +Subscribing to updates does not affect the number of messages received by a client. Any client attached to a channel with the `object-subscribe` capability automatically receives all object messages for that channel. Subscribing to updates on an object adds a listener that is called whenever the client receives updates for that object. + +## Cost optimization + +### Prefer flat data structures + +Setting a primitive value on a LiveMap costs one message. Creating a nested LiveMap costs two or more messages (one to create the object, one to assign it), plus additional messages for each level of nesting. Flatten your data model where possible to reduce the number of messages per update. + +### Minimize objects per channel + +Each object on a channel is synchronized when a client attaches or resynchronizes. Fewer objects per channel reduces the cost of synchronization. Distribute objects across channels based on which clients need access to them. + +## Synchronization + +During initial synchronization and resynchronization, Ably sends each object on the channel as a message. + +For example, if a channel contains 10 objects (such as `LiveMap` and `LiveCounter` instances), a client attaching to the channel receives 10 messages during synchronization. + +Similarly, if a client becomes disconnected and needs to resynchronize, it receives messages for each object that needs to be synchronized. + +Only [reachable](/docs/liveobjects/concepts/objects#reachability) objects are counted. Ably may send [tombstone](/docs/liveobjects/concepts/objects#tombstones) objects to the client, but these will not count towards your usage. + +## REST API + +The [LiveObjects REST API](/docs/liveobjects/rest-api-usage) also counts messages for operations performed. + +When fetching objects via the REST API, each instance of an [object type](/docs/liveobjects/concepts/objects#object-types) included in the response is counted as one message. + +Each operation published via the REST API counts as one message. When creating objects using the `path` field, the server constructs two messages (a create operation and a `MAP_SET` operation to assign it). diff --git a/src/pages/docs/livesync/pricing.mdx b/src/pages/docs/livesync/pricing.mdx new file mode 100644 index 0000000000..69db201ce6 --- /dev/null +++ b/src/pages/docs/livesync/pricing.mdx @@ -0,0 +1,37 @@ +--- +title: "LiveSync pricing" +meta_description: "Understand how LiveSync database change events contribute to your message count and strategies to optimize costs." +meta_keywords: "LiveSync pricing, message counting, database connector, realtime sync" +intro: "How LiveSync operations contribute to your message count and strategies to optimize costs." +--- + +[LiveSync](/docs/livesync) publishes database changes as Pub/Sub messages. Each update follows the standard inbound/outbound counting pattern. + +## LiveSync operations + +The following table shows how LiveSync operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| Database update publish | 1 inbound message | +| Update delivery | 1 outbound message per subscriber | + +For example, if the database connector publishes 1 update and 3 clients are subscribed, this counts as **4 messages** (1 inbound + 3 outbound). + + + +## Channels and connections + +Each channel that the database connector publishes to contributes to your [channel count](/docs/platform/pricing#channels). Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections) per minute of connection time. + +## Cost optimization + +### Minimize change frequency + +Configure your database connector to publish only meaningful changes. Reducing the frequency of updates at the source directly reduces your message count. + +### Limit subscriber count + +Only subscribe clients to channels where they need real-time updates. Fewer subscribers per channel reduces the number of outbound messages generated per update. diff --git a/src/pages/docs/platform/pricing/message-counting.mdx b/src/pages/docs/platform/pricing/message-counting.mdx new file mode 100644 index 0000000000..360bec6309 --- /dev/null +++ b/src/pages/docs/platform/pricing/message-counting.mdx @@ -0,0 +1,162 @@ +--- +title: "Message counting" +meta_description: "Understand which operations across all Ably products count as messages, including Pub/Sub, Chat, LiveObjects, Spaces, AI Transport, and LiveSync." +meta_keywords: "message counting, pricing, Pub/Sub messages, Chat messages, LiveObjects messages, Spaces messages, AI Transport messages, LiveSync messages" +intro: "Messages are the primary unit of consumption that Ably charges for. Understanding which operations count as messages helps you estimate and manage costs." +--- + +## How Ably counts messages + +As a general rule, most operations involve publishing and receiving messages. Each operation generates: + +* 1 inbound message when a client publishes or performs an operation +* 1 outbound message for each subscriber that receives it + +For example, if 1 client publishes a message to a channel with 10 subscribers, this counts as **11 messages** (1 inbound + 10 outbound). + +By default, [echo messages](/docs/pub-sub/advanced#echo) are enabled, which means the publisher also receives its own message as an outbound delivery. In the example above, echo adds 1 outbound message for a total of **12 messages**. + +Client-side [message filtering](/docs/api/realtime-sdk/channels#subscribe), where a subscribe callback selectively processes messages, does not reduce your message count. Ably delivers all messages on a channel to every subscriber regardless of client-side filtering. To filter messages server-side and reduce outbound message counts, use [subscription filters](/docs/pub-sub/advanced#subscription-filters). + +The tables below list all operations that count as messages. Unless noted otherwise, each operation follows this inbound/outbound pattern. `Time` and `Ping` operations are not counted. + +## Message size + +Ably calculates message size as the sum of the `name`, `clientId`, `data`, and `extras` [properties](/docs/api/realtime-sdk/messages#properties) before any compression or expansion occurs in the serialization process. This applies to all products built on Pub/Sub, including Chat. + +* `name` and `clientId` are calculated as the size in bytes of their UTF-8 representation. +* `data` is calculated as the size in bytes if it is binary, or its UTF-8 byte length if it is a string. +* `extras` is calculated as the string length of its JSON representation. + +Ably bills messages in 5KiB chunks. A message up to 5KiB counts as 1 message. A 50KiB message counts as 10 messages, and a 16KiB message counts as 4 messages. + +Each package has a bandwidth allowance based on an average message size of 5KiB. If your total bandwidth for the month exceeds this baseline, Ably charges overage per GiB. See [maximum message size](/docs/platform/pricing/limits#message) for the size limits per package type. + +## Pub/Sub operations + +The following table shows how [Pub/Sub](/docs/pub-sub) operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| [Publish](/docs/channels/messages#publish) | 1 inbound message | +| Message delivery | 1 outbound message per subscriber | +| [Presence enter](/docs/presence-occupancy/presence) | 1 inbound message | +| [Presence leave](/docs/presence-occupancy/presence) | 1 inbound message | +| [Presence update](/docs/presence-occupancy/presence) | 1 inbound message | +| Presence event delivery | 1 outbound message per presence subscriber | +| [Persistence](/docs/storage-history/storage) storage | 1 additional message per stored message | +| [History](/docs/storage-history/history) retrieval | 1 message per retrieved message | +| [Integration](/docs/platform/integrations) delivery | 1 outbound message per integration target | +| [Inbound integration](/docs/platform/integrations#inbound) publish | 1 inbound message | +| [Rewind](/docs/channels/options/rewind) on attach | 1 message per rewound message (up to 100) | +| [Push notification](/docs/push) delivery | 1 message per delivered notification | +| [Presence REST](/docs/presence-occupancy/presence) query | 1 message per member returned | +| [Batch presence](/docs/presence-occupancy/presence) request | 1 message per member across all queried channels | +| [Annotation](/docs/messages/annotations) publish | 1 inbound message | +| [Annotation](/docs/messages/annotations) delete | 1 inbound message | +| [Annotation summary](/docs/messages/annotations#annotation-summaries) delivery | 1 outbound message per subscriber; multiple annotations may be rolled up into a single summary | +| [Lifecycle event](/docs/metadata-stats/metadata) (`[meta]connection.lifecycle`, `[meta]channel.lifecycle`) | 1 message per event | +| [`[meta]stats:minute`](/docs/metadata-stats/metadata) event | 1 message per event | +| [`[meta]log`](/docs/metadata-stats/metadata) subscription | Not counted | + +For Pub/Sub-specific cost optimization strategies, see [Pub/Sub pricing](/docs/pub-sub/pricing). + +## Chat operations + +The [Chat SDK](/docs/chat) is built on top of [Pub/Sub](/docs/pub-sub). All Chat operations generate Pub/Sub messages that follow the same counting rules. + +| Operation | Messages counted | +| --- | --- | +| [Send message](/docs/chat/rooms/messages) | 1 inbound message | +| Message delivery | 1 outbound message per subscriber | +| [Update message](/docs/chat/rooms/messages#update) | 1 inbound message | +| Message update delivery | 1 outbound message per subscriber | +| [Delete message](/docs/chat/rooms/messages#delete) | 1 inbound message | +| Message deletion delivery | 1 outbound message per subscriber | +| [History](/docs/chat/rooms/history) retrieval | 1 message per retrieved message | +| [Typing indicator](/docs/chat/rooms/typing) keystroke | 1 inbound message; repeats every heartbeat interval (default 10s) | +| Typing indicator delivery | 1 outbound message per subscriber per heartbeat | +| [Typing indicator](/docs/chat/rooms/typing) stop | 1 inbound message | +| Typing stop delivery | 1 outbound message per subscriber | +| [Room reaction](/docs/chat/rooms/reactions) | 1 inbound message | +| Room reaction delivery | 1 outbound message per subscriber | +| [Message reaction](/docs/chat/rooms/message-reactions) send | 1 inbound message | +| [Message reaction](/docs/chat/rooms/message-reactions) delete | 1 inbound message | +| Message reaction summary delivery | 1 outbound message per subscriber; multiple reactions may be rolled up into a single summary | +| [Presence enter](/docs/chat/rooms/presence) | 1 inbound message | +| [Presence leave](/docs/chat/rooms/presence) | 1 inbound message | +| [Presence update](/docs/chat/rooms/presence) | 1 inbound message | +| Presence event delivery | 1 outbound message per presence subscriber | +| [Occupancy](/docs/chat/rooms/occupancy) event | 1 outbound message per subscriber (generated on membership changes, debounced up to 15s) | +| [Moderation](/docs/chat/moderation) action | 1 inbound message; triggers a message update or delete which follows standard delivery | + +For Chat-specific cost optimization strategies, see [Chat pricing](/docs/chat/pricing). + +## LiveObjects operations + +[LiveObjects](/docs/liveobjects) operations are billed using ObjectMessages. Each ObjectMessage follows the standard inbound/outbound counting pattern. + +| Operation | Messages counted | +| --- | --- | +| [LiveMap](/docs/liveobjects/map) set or remove | 1 inbound message per operation | +| [LiveMap](/docs/liveobjects/map) create (shallow) | 2 inbound messages (create + assign) | +| [LiveCounter](/docs/liveobjects/counter) increment or decrement | 1 inbound message | +| [LiveCounter](/docs/liveobjects/counter) create | 2 inbound messages (create + assign) | +| ObjectMessage delivery | 1 outbound message per connected client | +| [Synchronization](/docs/liveobjects/pricing#synchronization) | 1 message per object synchronized | +| [REST API](/docs/liveobjects/pricing#rest-api) fetch | 1 message per object in response | +| [Batch](/docs/liveobjects/batch) operation | 1 message per operation in the batch | + +Nested object creation generates additional messages for each nested object. + +For LiveObjects-specific cost optimization strategies, see [LiveObjects pricing](/docs/liveobjects/pricing). + +## Spaces operations + +The [Spaces SDK](/docs/spaces) is built on top of [Pub/Sub](/docs/pub-sub) channels and [presence](/docs/presence-occupancy/presence). All Spaces operations generate Pub/Sub messages that follow the same counting rules. + +| Operation | Messages counted | +| --- | --- | +| [Enter](/docs/spaces/space#enter) space | 1 inbound message | +| [Leave](/docs/spaces/space#leave) space | 1 inbound message | +| [Update profile](/docs/spaces/space#update-profile) | 1 inbound message | +| Space event delivery | 1 outbound message per subscriber | +| [Set location](/docs/spaces/locations#set) | 1 inbound message | +| Location event delivery | 1 outbound message per subscriber | +| [Set cursor position](/docs/spaces/cursors#set) | 1 inbound message per batch (default batch interval 25ms) | +| Cursor event delivery | 1 outbound message per subscriber | +| [Lock acquire](/docs/spaces/locking#acquire) | 1 inbound message | +| [Lock release](/docs/spaces/locking#release) | 1 inbound message | +| Lock event delivery | 1 outbound message per subscriber | + +Live cursors use a [separate channel](/docs/spaces/cursors#foundations) from other space features due to their high update frequency. Registering multiple subscription listeners for the same event does not increase your message count, as these are [client-side filtered events](/docs/spaces/space#subscribe). + +For Spaces-specific cost optimization strategies, see [Spaces pricing](/docs/spaces/pricing). + +## AI Transport operations + +[AI Transport](/docs/ai-transport) uses standard Pub/Sub messaging. Each token or message published to Ably counts as an inbound message, and each delivery to a subscriber counts as an outbound message. + +| Operation | Messages counted | +| --- | --- | +| Token publish (per-token) | 1 inbound message per token | +| Token publish (per-response with [append rollup](/docs/ai-transport/token-streaming/token-rate-limits#per-response)) | Multiple tokens conflated into fewer inbound messages | +| Token delivery | 1 outbound message per subscriber | +| [Persistence](/docs/storage-history/storage) storage | 1 additional message per stored message | + +The total cost depends on the [token streaming pattern](/docs/ai-transport/token-streaming#token-streaming-patterns) you choose. With per-token streaming, each token is a separate inbound message. With [message-per-response](/docs/ai-transport/token-streaming/message-per-response), append rollup conflates multiple tokens into fewer inbound messages, reducing costs. For example, 300 tokens can be conflated to approximately 100 inbound messages. + +For AI Transport-specific cost optimization strategies, see [AI Transport pricing](/docs/ai-transport/pricing). + +## LiveSync operations + +[LiveSync](/docs/livesync) publishes database changes as Pub/Sub messages. Each update follows the standard inbound/outbound counting pattern. + +| Operation | Messages counted | +| --- | --- | +| Database update publish | 1 inbound message | +| Update delivery | 1 outbound message per subscriber | + +For example, if the database connector publishes 1 update and 3 clients are subscribed, this counts as **4 messages** (1 inbound + 3 outbound). + +For LiveSync-specific cost optimization strategies, see [LiveSync pricing](/docs/livesync/pricing). diff --git a/src/pages/docs/pub-sub/pricing.mdx b/src/pages/docs/pub-sub/pricing.mdx new file mode 100644 index 0000000000..14c8746f47 --- /dev/null +++ b/src/pages/docs/pub-sub/pricing.mdx @@ -0,0 +1,73 @@ +--- +title: "Pub/Sub pricing" +meta_description: "Understand how Pub/Sub operations contribute to your message count, including persistence, presence, and cost optimization strategies." +meta_keywords: "message counting, pricing, persistence, presence, cost optimization, Pub/Sub pricing" +intro: "How Pub/Sub operations contribute to your message count and strategies to optimize costs." +--- + +Pub/Sub operations contribute to your message count based on publishing, delivery, and optional features like persistence and presence. + +## Pub/Sub operations + +The following table shows how Pub/Sub operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| [Publish](/docs/channels/messages#publish) | 1 inbound message | +| Message delivery | 1 outbound message per subscriber | +| [Presence enter](/docs/presence-occupancy/presence) | 1 inbound message | +| [Presence leave](/docs/presence-occupancy/presence) | 1 inbound message | +| [Presence update](/docs/presence-occupancy/presence) | 1 inbound message | +| Presence event delivery | 1 outbound message per presence subscriber | +| [Persistence](/docs/storage-history/storage) storage | 1 additional message per stored message | +| [History](/docs/storage-history/history) retrieval | 1 message per retrieved message | +| [Integration](/docs/platform/integrations) delivery | 1 outbound message per integration target | +| [Inbound integration](/docs/platform/integrations#inbound) publish | 1 inbound message | +| [Rewind](/docs/channels/options/rewind) on attach | 1 message per rewound message (up to 100) | +| [Push notification](/docs/push) delivery | 1 message per delivered notification | +| [Presence REST](/docs/presence-occupancy/presence) query | 1 message per member returned | +| [Batch presence](/docs/presence-occupancy/presence) request | 1 message per member across all queried channels | +| [Annotation](/docs/messages/annotations) publish | 1 inbound message | +| [Annotation](/docs/messages/annotations) delete | 1 inbound message | +| [Annotation summary](/docs/messages/annotations#annotation-summaries) delivery | 1 outbound message per subscriber; multiple annotations may be rolled up into a single summary | +| [Lifecycle event](/docs/metadata-stats/metadata) (`[meta]connection.lifecycle`, `[meta]channel.lifecycle`) | 1 message per event | +| [`[meta]stats:minute`](/docs/metadata-stats/metadata) event | 1 message per event | +| [`[meta]log`](/docs/metadata-stats/metadata) subscription | Not counted | + + + +## Channels and connection minutes + +[Channels](/docs/channels) are the unit of message distribution. Each channel you use contributes to your [channel count](/docs/platform/pricing#channels). Ably bills [connection minutes](/docs/platform/pricing#connections) per minute of connection time for each connected client. + +## Cost optimization + +### Use ephemeral messages + +Mark messages as [ephemeral](/docs/pub-sub/advanced#ephemeral) to exempt them from persistence, rewind, resume, and integrations. Use this for streaming data where history is not needed. + +### Disable self-delivery + +Set [echoMessages: false](/docs/pub-sub/advanced#echo) to prevent messages from being delivered back to the publisher. This is useful for optimistic UI patterns and server-side publishers. + +### Enable conflation + +[Conflation](/docs/guides/pub-sub/data-streaming#solution-message-conflation) delivers only the latest message per key in each time window. Use this for high-frequency updates where only the latest value matters. + +### Use server-side batching + +[Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) groups messages into single deliveries. Use this for high-throughput channels where slight delay is acceptable. + +### Enable delta compression + +[Delta compression](/docs/channels/options/deltas) reduces payload size, lowering bandwidth costs. Use this for large, structurally similar successive messages. + +### Use occupancy instead of presence + +Use [occupancy](/docs/presence-occupancy/occupancy) instead of presence when you only need member counts, not individual identities. This avoids the n-squared presence event fan-out where each member event is delivered to every subscriber. For example, 200 members joining and leaving a channel generates approximately **80,400 messages**. See [large-scale presence sets](/docs/presence-occupancy/presence#large-presence) for the full calculation. + +### Enable server-side presence batching + +Enable [server-side batching](/docs/presence-occupancy/presence#large-presence) to group presence events and support up to 20,000 members per channel. Subscribe to presence updates only on channels where you need member-level detail. diff --git a/src/pages/docs/spaces/pricing.mdx b/src/pages/docs/spaces/pricing.mdx new file mode 100644 index 0000000000..c5236c2a08 --- /dev/null +++ b/src/pages/docs/spaces/pricing.mdx @@ -0,0 +1,50 @@ +--- +title: "Spaces pricing" +meta_description: "Understand how Spaces SDK features contribute to your message count, including avatar stacks, member location, live cursors, and component locking." +meta_keywords: "Spaces pricing, message counting, live cursors, avatar stacks, component locking" +intro: "How Spaces SDK features contribute to your message count and strategies to optimize costs." +--- + +The [Spaces SDK](/docs/spaces) is built on top of [Pub/Sub](/docs/pub-sub) channels and [presence](/docs/presence-occupancy/presence). All Spaces operations generate Pub/Sub messages that follow the same counting rules. + +## Spaces operations + +The following table shows how Spaces operations contribute to your message count: + +| Operation | Messages counted | +| --- | --- | +| [Enter](/docs/spaces/space#enter) space | 1 inbound message | +| [Leave](/docs/spaces/space#leave) space | 1 inbound message | +| [Update profile](/docs/spaces/space#update-profile) | 1 inbound message | +| Space event delivery | 1 outbound message per subscriber | +| [Set location](/docs/spaces/locations#set) | 1 inbound message | +| Location event delivery | 1 outbound message per subscriber | +| [Set cursor position](/docs/spaces/cursors#set) | 1 inbound message per batch (default batch interval 25ms) | +| Cursor event delivery | 1 outbound message per subscriber | +| [Lock acquire](/docs/spaces/locking#acquire) | 1 inbound message | +| [Lock release](/docs/spaces/locking#release) | 1 inbound message | +| Lock event delivery | 1 outbound message per subscriber | + +Registering multiple subscription listeners for the same event does not increase your message count. These are [client-side filtered events](/docs/spaces/space#subscribe) where only a single message is published per event by Ably. + + + +## Channels and connections + +Each space maps to an underlying Pub/Sub [channel](/docs/channels). Live cursors use a [separate channel](/docs/spaces/cursors#foundations) from other space features due to their high update frequency. Both channels contribute to your [channel count](/docs/platform/pricing#channels). Ably bills [connection minutes](/docs/platform/pricing#connections) per minute of connection time for each connected client. + +## Cost optimization + +### Increase cursor batch interval + +Increase the [`outboundBatchInterval`](/docs/spaces/cursors#batch) to reduce the frequency of cursor position updates. The default is 25ms. Increasing this value reduces the number of messages at the cost of less smooth cursor movement. + +### Limit concurrent cursor streaming + +Ably recommends a maximum of [20 members](/docs/spaces/cursors) simultaneously streaming their cursors in a space for an optimal end-user experience. Beyond this, the volume of cursor messages increases significantly. + +### Use occupancy instead of presence for large spaces + +For spaces with many members where you only need a count of active users, use [occupancy](/docs/presence-occupancy/occupancy) on the underlying channel instead of tracking individual presence events.