From 4a0cdaabcf4ded0b03be0848516c9aa77ca9476f Mon Sep 17 00:00:00 2001 From: umair Date: Mon, 2 Mar 2026 09:47:28 +0000 Subject: [PATCH 1/7] Addresses message billing docs gap --- src/data/nav/platform.ts | 4 + .../docs/platform/pricing/message-billing.mdx | 102 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/pages/docs/platform/pricing/message-billing.mdx diff --git a/src/data/nav/platform.ts b/src/data/nav/platform.ts index 8e1df1417d..c37ed69151 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-billing', + name: 'Message billing', + }, { link: '/docs/platform/pricing/limits', name: 'Limits', diff --git a/src/pages/docs/platform/pricing/message-billing.mdx b/src/pages/docs/platform/pricing/message-billing.mdx new file mode 100644 index 0000000000..4ace7e2532 --- /dev/null +++ b/src/pages/docs/platform/pricing/message-billing.mdx @@ -0,0 +1,102 @@ +--- +title: "Message billing" +meta_description: "Understand how different Ably operations contribute to your message count, including persistence, presence, Chat SDK features, and cost optimization strategies." +meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, chat billing, cost optimization" +--- + +This page explains how different Ably operations contribute to your message count. Understanding message billing helps you optimize costs and choose the right features for your use case. + + + +## Basic message counting + +Every message sent through Ably is counted for both publishing and delivery: + +* 1 publish = 1 inbound message +* Each subscriber delivery = 1 outbound message per subscriber + +For example, 1 publisher sends a message to a channel with 10 subscribers = **11 messages** (1 inbound + 10 outbound). + +## Message size + +Message size is calculated 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. + +* `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. + +The [bandwidth](/docs/platform/pricing#bandwidth) allowance calculation uses an average message size of 5KiB. If your total bandwidth for the month exceeds this baseline, the overage is charged per GiB. + +See [maximum message size](/docs/platform/pricing/limits#message) for the size limits per package type. + +## Operations and their billing impact + +The following table shows how different operations contribute to your message count: + +| Operation | Messages counted | Notes | +| --- | --- | --- | +| Publish to channel | 1 inbound + 1 per subscriber outbound | Base cost for all messaging | +| Persisted message storage | +1 per message stored | Enable via [channel rules](/docs/storage-history/storage) | +| History retrieval | +1 per message retrieved | Each call to the [history API](/docs/storage-history/history) | +| Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same counting as regular messages | +| Typing indicator heartbeat | 1 inbound + 1 per room member outbound | Controlled by `heartbeatThrottleMs` | +| Webhook/integration delivery | +1 per outbound delivery | Per [integration](/docs/platform/integrations) target | +| Rewind on attach | Rewound messages count as outbound | Up to 100 messages per [rewind](/docs/channels/options/rewind) | +| Push notification | Counted separately per push platform | See [push notifications](/docs/push) | + +## Persistence billing + +When persistence is enabled via [channel rules](/docs/storage-history/storage), each message stored to disk counts as an additional message. + +If you persist all messages, a message published to 10 subscribers with persistence enabled = 1 (publish) + 10 (subscriber delivery) + 1 (persistence storage) = **12 messages**. + +If you persist last message only, the most recent message is stored. The storage cost is 1 additional message per new "last message" stored. + +Retrieving persisted messages via the [history API](/docs/storage-history/history) also counts: each message returned in a history response = 1 additional message. + +To avoid persistence costs on messages that don't need history, mark them as [ephemeral](/docs/pub-sub/advanced#ephemeral). + +## Presence event billing + +Presence events (enter, leave, update) are billed as regular messages: + +* 1 presence enter event with 100 presence subscribers = **101 messages** (1 enter + 100 deliveries) + +In high-churn scenarios, presence events can generate significant message volume due to the n-squared pattern. When both subscribers and members are present on a channel, each member event is delivered to every subscriber. + +For example, 200 members joining and leaving a channel generates approximately **80,400 messages**. See the [large-scale presence sets](/docs/presence-occupancy/presence#large-presence) section for the full calculation. + +To reduce presence costs: + +* Use [occupancy](/docs/presence-occupancy/occupancy) if you only need member counts, not individual identities. +* 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. + +## Chat-specific billing + +The [Chat SDK](/docs/chat) uses the same billing model. + +Each chat message published to a room = 1 inbound + 1 per subscriber outbound. + +Each typing indicator heartbeat event = 1 inbound + 1 per subscriber outbound. At the default 10-second heartbeat, one active typer generates approximately six events per minute. In a room with 50 subscribers, this is approximately **306 messages per minute** per active typer. + +Each reaction = 1 inbound + 1 per subscriber outbound. + +## Cost optimization strategies + +The following table summarizes strategies for reducing message costs: + +| Strategy | How it reduces costs | When to use | +| --- | --- | --- | +| [Ephemeral messages](/docs/pub-sub/advanced#ephemeral) | Exempt from persistence, rewind, resume, and integrations | Streaming data where history is not needed | +| [echoMessages: false](/docs/pub-sub/advanced#echo) | Prevents self-delivery to the publisher | Optimistic UI patterns and server-side publishers | +| [Conflation](/docs/guides/pub-sub/data-streaming#solution-message-conflation) | Delivers only the latest message per key in each time window | High-frequency updates where only the latest value matters | +| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | High-throughput channels where slight delay is acceptable | +| [Delta compression](/docs/channels/options/deltas) | Reduces payload size, lowering bandwidth costs | Large, structurally similar successive messages | +| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out | When you need member counts, not individual identities | + + From 8740f058ac2f28ae7bf5a282fa4d7a8a5e7fb4dd Mon Sep 17 00:00:00 2001 From: umair Date: Tue, 3 Mar 2026 12:15:01 +0000 Subject: [PATCH 2/7] separates Chat and pubsub billing concerns --- src/data/nav/chat.ts | 4 ++ src/data/nav/platform.ts | 4 -- src/data/nav/pubsub.ts | 4 ++ src/pages/docs/chat/message-billing.mdx | 42 +++++++++++++++++++ .../pricing => pub-sub}/message-billing.mdx | 27 +++++------- 5 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 src/pages/docs/chat/message-billing.mdx rename src/pages/docs/{platform/pricing => pub-sub}/message-billing.mdx (80%) diff --git a/src/data/nav/chat.ts b/src/data/nav/chat.ts index af612eee13..d8991918de 100644 --- a/src/data/nav/chat.ts +++ b/src/data/nav/chat.ts @@ -223,6 +223,10 @@ export default { }, ], }, + { + name: 'Message billing', + link: '/docs/chat/message-billing', + }, { name: 'Guides', pages: [ diff --git a/src/data/nav/platform.ts b/src/data/nav/platform.ts index c37ed69151..8e1df1417d 100644 --- a/src/data/nav/platform.ts +++ b/src/data/nav/platform.ts @@ -125,10 +125,6 @@ export default { link: '/docs/platform/pricing/billing', name: 'Billing', }, - { - link: '/docs/platform/pricing/message-billing', - name: 'Message billing', - }, { link: '/docs/platform/pricing/limits', name: 'Limits', diff --git a/src/data/nav/pubsub.ts b/src/data/nav/pubsub.ts index 6936a8f9ab..7d5a822bd3 100644 --- a/src/data/nav/pubsub.ts +++ b/src/data/nav/pubsub.ts @@ -356,6 +356,10 @@ export default { }, ], }, + { + name: 'Message billing', + link: '/docs/pub-sub/message-billing', + }, { name: 'Guides', pages: [ diff --git a/src/pages/docs/chat/message-billing.mdx b/src/pages/docs/chat/message-billing.mdx new file mode 100644 index 0000000000..1952446d12 --- /dev/null +++ b/src/pages/docs/chat/message-billing.mdx @@ -0,0 +1,42 @@ +--- +title: "Message billing" +meta_description: "Understand how Chat SDK features contribute to your message count, including messages, typing indicators, reactions, and cost optimization strategies." +meta_keywords: "chat billing, message counting, typing indicators, reactions, chat pricing, cost optimization" +--- + +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 [message billing](/docs/pub-sub/message-billing) rules. This page covers billing considerations specific to Chat SDK features. + +## Chat message billing + +Each chat message published to a room = 1 inbound + 1 per subscriber outbound. + +For example, 1 message sent in a room with 50 subscribers = **51 messages** (1 inbound + 50 outbound). + +## Typing indicator billing + +Each typing indicator heartbeat event = 1 inbound + 1 per subscriber outbound. At the default 10-second heartbeat, one active typer generates approximately six events per minute. An explicit stop event is also sent when typing ends, adding 1 additional inbound + 1 per subscriber outbound. + +In a room with 50 subscribers, this is approximately **306 messages per minute** per active typer, plus the stop event. + +To reduce typing indicator costs: + +* Increase `heartbeatThrottleMs` to reduce event frequency. +* Disable typing indicators in rooms where they are not needed. + +## Reaction billing + +Each reaction = 1 inbound + 1 per subscriber outbound. + +## Cost optimization + +The following strategies reduce Chat message costs: + +| Strategy | How it reduces costs | +| --- | --- | +| Increase `heartbeatThrottleMs` | Reduces typing indicator event frequency | +| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | +| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out when you only need member counts | + + diff --git a/src/pages/docs/platform/pricing/message-billing.mdx b/src/pages/docs/pub-sub/message-billing.mdx similarity index 80% rename from src/pages/docs/platform/pricing/message-billing.mdx rename to src/pages/docs/pub-sub/message-billing.mdx index 4ace7e2532..97c1613f32 100644 --- a/src/pages/docs/platform/pricing/message-billing.mdx +++ b/src/pages/docs/pub-sub/message-billing.mdx @@ -1,7 +1,7 @@ --- title: "Message billing" -meta_description: "Understand how different Ably operations contribute to your message count, including persistence, presence, Chat SDK features, and cost optimization strategies." -meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, chat billing, cost optimization" +meta_description: "Understand how different Ably Pub/Sub operations contribute to your message count, including persistence, presence, and cost optimization strategies." +meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, cost optimization" --- This page explains how different Ably operations contribute to your message count. Understanding message billing helps you optimize costs and choose the right features for your use case. @@ -16,12 +16,18 @@ Every message sent through Ably is counted for both publishing and delivery: * 1 publish = 1 inbound message * Each subscriber delivery = 1 outbound message per subscriber +* Each integration delivery = 1 outbound message per [integration](/docs/platform/integrations) target +* Messages from metachannels (such as `[meta]log` and `[meta]stats`) follow the same counting rules For example, 1 publisher sends a message to a channel with 10 subscribers = **11 messages** (1 inbound + 10 outbound). ## Message size -Message size is calculated 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. +Message size for Pub/Sub messages is calculated 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. + + * `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. @@ -39,9 +45,8 @@ The following table shows how different operations contribute to your message co | --- | --- | --- | | Publish to channel | 1 inbound + 1 per subscriber outbound | Base cost for all messaging | | Persisted message storage | +1 per message stored | Enable via [channel rules](/docs/storage-history/storage) | -| History retrieval | +1 per message retrieved | Each call to the [history API](/docs/storage-history/history) | +| History retrieval | +1 per message retrieved | Each message returned in a [history](/docs/storage-history/history) response counts as 1 outbound message | | Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same counting as regular messages | -| Typing indicator heartbeat | 1 inbound + 1 per room member outbound | Controlled by `heartbeatThrottleMs` | | Webhook/integration delivery | +1 per outbound delivery | Per [integration](/docs/platform/integrations) target | | Rewind on attach | Rewound messages count as outbound | Up to 100 messages per [rewind](/docs/channels/options/rewind) | | Push notification | Counted separately per push platform | See [push notifications](/docs/push) | @@ -56,7 +61,7 @@ If you persist last message only, the most recent message is stored. The storage Retrieving persisted messages via the [history API](/docs/storage-history/history) also counts: each message returned in a history response = 1 additional message. -To avoid persistence costs on messages that don't need history, mark them as [ephemeral](/docs/pub-sub/advanced#ephemeral). +To avoid additional costs for messages that do not need persistence, history, rewind, integrations, or resume, mark them as [ephemeral](/docs/pub-sub/advanced#ephemeral). Ephemeral messages are exempt from all of these features, reducing your billable message count. ## Presence event billing @@ -74,16 +79,6 @@ To reduce presence costs: * 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. -## Chat-specific billing - -The [Chat SDK](/docs/chat) uses the same billing model. - -Each chat message published to a room = 1 inbound + 1 per subscriber outbound. - -Each typing indicator heartbeat event = 1 inbound + 1 per subscriber outbound. At the default 10-second heartbeat, one active typer generates approximately six events per minute. In a room with 50 subscribers, this is approximately **306 messages per minute** per active typer. - -Each reaction = 1 inbound + 1 per subscriber outbound. - ## Cost optimization strategies The following table summarizes strategies for reducing message costs: From 5e125be756aa1479c41c5b861c4d7737cf8b3ebb Mon Sep 17 00:00:00 2001 From: umair Date: Wed, 11 Mar 2026 17:19:09 +0000 Subject: [PATCH 3/7] Centralise message billing into platform pricing and add product-specific pricing pages --- src/data/nav/chat.ts | 4 +- src/data/nav/platform.ts | 4 + src/data/nav/pubsub.ts | 4 +- src/pages/docs/chat/message-billing.mdx | 42 ---------- src/pages/docs/chat/pricing.mdx | 39 ++++++++++ .../pricing}/message-billing.mdx | 76 +++++++++++-------- src/pages/docs/pub-sub/pricing.mdx | 41 ++++++++++ 7 files changed, 133 insertions(+), 77 deletions(-) delete mode 100644 src/pages/docs/chat/message-billing.mdx create mode 100644 src/pages/docs/chat/pricing.mdx rename src/pages/docs/{pub-sub => platform/pricing}/message-billing.mdx (53%) create mode 100644 src/pages/docs/pub-sub/pricing.mdx diff --git a/src/data/nav/chat.ts b/src/data/nav/chat.ts index d8991918de..ecb8f9abab 100644 --- a/src/data/nav/chat.ts +++ b/src/data/nav/chat.ts @@ -224,8 +224,8 @@ export default { ], }, { - name: 'Message billing', - link: '/docs/chat/message-billing', + name: 'Pricing', + link: '/docs/chat/pricing', }, { name: 'Guides', diff --git a/src/data/nav/platform.ts b/src/data/nav/platform.ts index 8e1df1417d..c37ed69151 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-billing', + name: 'Message billing', + }, { link: '/docs/platform/pricing/limits', name: 'Limits', diff --git a/src/data/nav/pubsub.ts b/src/data/nav/pubsub.ts index 7d5a822bd3..5b56d5c5be 100644 --- a/src/data/nav/pubsub.ts +++ b/src/data/nav/pubsub.ts @@ -357,8 +357,8 @@ export default { ], }, { - name: 'Message billing', - link: '/docs/pub-sub/message-billing', + name: 'Pricing', + link: '/docs/pub-sub/pricing', }, { name: 'Guides', diff --git a/src/pages/docs/chat/message-billing.mdx b/src/pages/docs/chat/message-billing.mdx deleted file mode 100644 index 1952446d12..0000000000 --- a/src/pages/docs/chat/message-billing.mdx +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: "Message billing" -meta_description: "Understand how Chat SDK features contribute to your message count, including messages, typing indicators, reactions, and cost optimization strategies." -meta_keywords: "chat billing, message counting, typing indicators, reactions, chat pricing, cost optimization" ---- - -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 [message billing](/docs/pub-sub/message-billing) rules. This page covers billing considerations specific to Chat SDK features. - -## Chat message billing - -Each chat message published to a room = 1 inbound + 1 per subscriber outbound. - -For example, 1 message sent in a room with 50 subscribers = **51 messages** (1 inbound + 50 outbound). - -## Typing indicator billing - -Each typing indicator heartbeat event = 1 inbound + 1 per subscriber outbound. At the default 10-second heartbeat, one active typer generates approximately six events per minute. An explicit stop event is also sent when typing ends, adding 1 additional inbound + 1 per subscriber outbound. - -In a room with 50 subscribers, this is approximately **306 messages per minute** per active typer, plus the stop event. - -To reduce typing indicator costs: - -* Increase `heartbeatThrottleMs` to reduce event frequency. -* Disable typing indicators in rooms where they are not needed. - -## Reaction billing - -Each reaction = 1 inbound + 1 per subscriber outbound. - -## Cost optimization - -The following strategies reduce Chat message costs: - -| Strategy | How it reduces costs | -| --- | --- | -| Increase `heartbeatThrottleMs` | Reduces typing indicator event frequency | -| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | -| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out when you only need member counts | - - diff --git a/src/pages/docs/chat/pricing.mdx b/src/pages/docs/chat/pricing.mdx new file mode 100644 index 0000000000..8a0004cad2 --- /dev/null +++ b/src/pages/docs/chat/pricing.mdx @@ -0,0 +1,39 @@ +--- +title: "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 billing, message counting, typing indicators, reactions, chat pricing, cost optimization" +intro: "How Chat SDK features contribute to your message count and strategies to optimize costs." +redirect_from: + - /docs/chat/message-billing +--- + +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 billing rules. + + + +## What counts as a message + +Each Chat feature generates Pub/Sub messages. For example, 1 message sent in a room with 50 subscribers counts as **51 messages** (1 inbound + 50 outbound). Typing indicators are the most significant cost driver. In a room with 50 subscribers, one active typer generates approximately **306 messages per minute** from heartbeat events alone, plus a stop event. + +For the complete breakdown of how each Chat operation counts as messages, see the [Chat operations](/docs/platform/pricing/message-billing#chat-operations) table in the central message billing reference. + +## Channels and connection minutes + +Chat rooms map directly to Pub/Sub channels. Each room feature (messages, typing, reactions, presence, occupancy) uses underlying Pub/Sub channels. Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections) in the same way as Pub/Sub. + +## Cost optimization strategies + +The following strategies reduce Chat message costs: + +| Strategy | How it reduces costs | +| --- | --- | +| Increase `heartbeatThrottleMs` | Reduces typing indicator event frequency | +| Disable typing indicators | Eliminates typing indicator messages in rooms where they are not needed | +| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | +| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out when you only need member counts | + + diff --git a/src/pages/docs/pub-sub/message-billing.mdx b/src/pages/docs/platform/pricing/message-billing.mdx similarity index 53% rename from src/pages/docs/pub-sub/message-billing.mdx rename to src/pages/docs/platform/pricing/message-billing.mdx index 97c1613f32..1c3450ed02 100644 --- a/src/pages/docs/pub-sub/message-billing.mdx +++ b/src/pages/docs/platform/pricing/message-billing.mdx @@ -1,45 +1,42 @@ --- title: "Message billing" -meta_description: "Understand how different Ably Pub/Sub operations contribute to your message count, including persistence, presence, and cost optimization strategies." -meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, cost optimization" +meta_description: "Understand how operations across all Ably products contribute to your message count, including Pub/Sub, Chat, LiveObjects, and Annotations." +meta_keywords: "message billing, message counting, pricing, Pub/Sub billing, Chat billing, LiveObjects billing" +intro: "How operations across all Ably products contribute to your message count." --- -This page explains how different Ably operations contribute to your message count. Understanding message billing helps you optimize costs and choose the right features for your use case. +All Ably products count messages for billing purposes. The following sections explain how operations across Pub/Sub, Chat, LiveObjects, and Annotations contribute to your message count. -## Basic message counting +## How messages are counted -Every message sent through Ably is counted for both publishing and delivery: +Ably counts every message for both publishing and delivery: * 1 publish = 1 inbound message * Each subscriber delivery = 1 outbound message per subscriber * Each integration delivery = 1 outbound message per [integration](/docs/platform/integrations) target * Messages from metachannels (such as `[meta]log` and `[meta]stats`) follow the same counting rules -For example, 1 publisher sends a message to a channel with 10 subscribers = **11 messages** (1 inbound + 10 outbound). +For example, if 1 publisher sends a message to a channel with 10 subscribers, this counts as **11 messages** (1 inbound + 10 outbound). ## Message size -Message size for Pub/Sub messages is calculated 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. - - +Ably calculates message size for Pub/Sub messages 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. * `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. -The [bandwidth](/docs/platform/pricing#bandwidth) allowance calculation uses an average message size of 5KiB. If your total bandwidth for the month exceeds this baseline, the overage is charged per GiB. +The [bandwidth](/docs/platform/pricing#bandwidth) allowance calculation uses 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. -## Operations and their billing impact +## Pub/Sub operations -The following table shows how different operations contribute to your message count: +The following table shows how Pub/Sub operations contribute to your message count: | Operation | Messages counted | Notes | | --- | --- | --- | @@ -51,13 +48,43 @@ The following table shows how different operations contribute to your message co | Rewind on attach | Rewound messages count as outbound | Up to 100 messages per [rewind](/docs/channels/options/rewind) | | Push notification | Counted separately per push platform | See [push notifications](/docs/push) | +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 | Notes | +| --- | --- | --- | +| Chat message | 1 inbound + 1 per subscriber outbound | Same as a standard publish | +| Typing indicator heartbeat | 1 inbound + 1 per subscriber outbound | Default interval is 10 seconds; approximately 6 events per minute per active typer | +| Typing indicator stop | 1 inbound + 1 per subscriber outbound | Sent when a user stops typing | +| Room reaction | 1 inbound + 1 per subscriber outbound | Same as a standard publish | +| Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same as Pub/Sub presence | +| Occupancy | Uses metachannels | Follows standard metachannel counting rules | + +For Chat-specific cost optimization strategies, see [Chat pricing](/docs/chat/pricing). + +## LiveObjects operations + +LiveObjects has a different billing scheme based on ObjectMessages. Each ObjectMessage counts as: + +* 1 outbound message per connected client (delivery) +* 1 inbound message per connected client (acknowledgment) + +See the [LiveObjects billing](/docs/liveobjects/concepts/billing) page for details. + +## Annotations + +[Annotations](/docs/messages/annotations) have a different billing scheme from standard messages. See the [Annotations](/docs/messages/annotations) page for details on how annotation operations are counted. + ## Persistence billing -When persistence is enabled via [channel rules](/docs/storage-history/storage), each message stored to disk counts as an additional message. +When persistence is enabled via [channel rules](/docs/storage-history/storage), Ably counts each message stored to disk as an additional message. -If you persist all messages, a message published to 10 subscribers with persistence enabled = 1 (publish) + 10 (subscriber delivery) + 1 (persistence storage) = **12 messages**. +If you persist all messages, a message published to 10 subscribers with persistence enabled counts as 1 (publish) + 10 (subscriber delivery) + 1 (persistence storage) = **12 messages**. -If you persist last message only, the most recent message is stored. The storage cost is 1 additional message per new "last message" stored. +If you persist the last message only, Ably stores the most recent message. The storage cost is 1 additional message per new "last message" stored. Retrieving persisted messages via the [history API](/docs/storage-history/history) also counts: each message returned in a history response = 1 additional message. @@ -65,7 +92,7 @@ To avoid additional costs for messages that do not need persistence, history, re ## Presence event billing -Presence events (enter, leave, update) are billed as regular messages: +Ably bills presence events (enter, leave, update) as regular messages: * 1 presence enter event with 100 presence subscribers = **101 messages** (1 enter + 100 deliveries) @@ -79,19 +106,6 @@ To reduce presence costs: * 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. -## Cost optimization strategies - -The following table summarizes strategies for reducing message costs: - -| Strategy | How it reduces costs | When to use | -| --- | --- | --- | -| [Ephemeral messages](/docs/pub-sub/advanced#ephemeral) | Exempt from persistence, rewind, resume, and integrations | Streaming data where history is not needed | -| [echoMessages: false](/docs/pub-sub/advanced#echo) | Prevents self-delivery to the publisher | Optimistic UI patterns and server-side publishers | -| [Conflation](/docs/guides/pub-sub/data-streaming#solution-message-conflation) | Delivers only the latest message per key in each time window | High-frequency updates where only the latest value matters | -| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | High-throughput channels where slight delay is acceptable | -| [Delta compression](/docs/channels/options/deltas) | Reduces payload size, lowering bandwidth costs | Large, structurally similar successive messages | -| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out | When you need member counts, not individual identities | - diff --git a/src/pages/docs/pub-sub/pricing.mdx b/src/pages/docs/pub-sub/pricing.mdx new file mode 100644 index 0000000000..d5e773c2f6 --- /dev/null +++ b/src/pages/docs/pub-sub/pricing.mdx @@ -0,0 +1,41 @@ +--- +title: "Pricing" +meta_description: "Understand how Pub/Sub operations contribute to your message count, including persistence, presence, and cost optimization strategies." +meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, cost optimization" +intro: "How Pub/Sub operations contribute to your message count and strategies to optimize costs." +redirect_from: + - /docs/pub-sub/message-billing +--- + +Pub/Sub operations contribute to your message count based on publishing, delivery, and optional features like persistence and presence. The following sections explain how Ably counts these operations and how to optimize costs. + + + +## What counts as a message + +Every message published through Pub/Sub counts as 1 inbound message, with each subscriber delivery counting as 1 outbound message. Optional features like persistence, history retrieval, and integrations generate additional messages. + +For the complete breakdown of how each Pub/Sub operation counts as messages, see the [Pub/Sub operations](/docs/platform/pricing/message-billing#pubsub-operations) table in the central message billing reference. + +## 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 strategies + +The following table summarizes strategies for reducing message costs: + +| Strategy | How it reduces costs | When to use | +| --- | --- | --- | +| [Ephemeral messages](/docs/pub-sub/advanced#ephemeral) | Exempt from persistence, rewind, resume, and integrations | Streaming data where history is not needed | +| [echoMessages: false](/docs/pub-sub/advanced#echo) | Prevents self-delivery to the publisher | Optimistic UI patterns and server-side publishers | +| [Conflation](/docs/guides/pub-sub/data-streaming#solution-message-conflation) | Delivers only the latest message per key in each time window | High-frequency updates where only the latest value matters | +| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | High-throughput channels where slight delay is acceptable | +| [Delta compression](/docs/channels/options/deltas) | Reduces payload size, lowering bandwidth costs | Large, structurally similar successive messages | +| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out | When you need member counts, not individual identities | + + From 5e025c1f96ec6e131451e4e6c0dcecc4054610eb Mon Sep 17 00:00:00 2001 From: umair Date: Thu, 12 Mar 2026 11:54:39 +0000 Subject: [PATCH 4/7] Expand central message-counting page with all products and detailed operations --- .../docs/platform/pricing/message-billing.mdx | 111 ------------- .../platform/pricing/message-counting.mdx | 156 ++++++++++++++++++ 2 files changed, 156 insertions(+), 111 deletions(-) delete mode 100644 src/pages/docs/platform/pricing/message-billing.mdx create mode 100644 src/pages/docs/platform/pricing/message-counting.mdx diff --git a/src/pages/docs/platform/pricing/message-billing.mdx b/src/pages/docs/platform/pricing/message-billing.mdx deleted file mode 100644 index 1c3450ed02..0000000000 --- a/src/pages/docs/platform/pricing/message-billing.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: "Message billing" -meta_description: "Understand how operations across all Ably products contribute to your message count, including Pub/Sub, Chat, LiveObjects, and Annotations." -meta_keywords: "message billing, message counting, pricing, Pub/Sub billing, Chat billing, LiveObjects billing" -intro: "How operations across all Ably products contribute to your message count." ---- - -All Ably products count messages for billing purposes. The following sections explain how operations across Pub/Sub, Chat, LiveObjects, and Annotations contribute to your message count. - - - -## How messages are counted - -Ably counts every message for both publishing and delivery: - -* 1 publish = 1 inbound message -* Each subscriber delivery = 1 outbound message per subscriber -* Each integration delivery = 1 outbound message per [integration](/docs/platform/integrations) target -* Messages from metachannels (such as `[meta]log` and `[meta]stats`) follow the same counting rules - -For example, if 1 publisher sends a message to a channel with 10 subscribers, this counts as **11 messages** (1 inbound + 10 outbound). - -## Message size - -Ably calculates message size for Pub/Sub messages 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. - -* `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. - -The [bandwidth](/docs/platform/pricing#bandwidth) allowance calculation uses 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 operations contribute to your message count: - -| Operation | Messages counted | Notes | -| --- | --- | --- | -| Publish to channel | 1 inbound + 1 per subscriber outbound | Base cost for all messaging | -| Persisted message storage | +1 per message stored | Enable via [channel rules](/docs/storage-history/storage) | -| History retrieval | +1 per message retrieved | Each message returned in a [history](/docs/storage-history/history) response counts as 1 outbound message | -| Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same counting as regular messages | -| Webhook/integration delivery | +1 per outbound delivery | Per [integration](/docs/platform/integrations) target | -| Rewind on attach | Rewound messages count as outbound | Up to 100 messages per [rewind](/docs/channels/options/rewind) | -| Push notification | Counted separately per push platform | See [push notifications](/docs/push) | - -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 | Notes | -| --- | --- | --- | -| Chat message | 1 inbound + 1 per subscriber outbound | Same as a standard publish | -| Typing indicator heartbeat | 1 inbound + 1 per subscriber outbound | Default interval is 10 seconds; approximately 6 events per minute per active typer | -| Typing indicator stop | 1 inbound + 1 per subscriber outbound | Sent when a user stops typing | -| Room reaction | 1 inbound + 1 per subscriber outbound | Same as a standard publish | -| Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same as Pub/Sub presence | -| Occupancy | Uses metachannels | Follows standard metachannel counting rules | - -For Chat-specific cost optimization strategies, see [Chat pricing](/docs/chat/pricing). - -## LiveObjects operations - -LiveObjects has a different billing scheme based on ObjectMessages. Each ObjectMessage counts as: - -* 1 outbound message per connected client (delivery) -* 1 inbound message per connected client (acknowledgment) - -See the [LiveObjects billing](/docs/liveobjects/concepts/billing) page for details. - -## Annotations - -[Annotations](/docs/messages/annotations) have a different billing scheme from standard messages. See the [Annotations](/docs/messages/annotations) page for details on how annotation operations are counted. - -## Persistence billing - -When persistence is enabled via [channel rules](/docs/storage-history/storage), Ably counts each message stored to disk as an additional message. - -If you persist all messages, a message published to 10 subscribers with persistence enabled counts as 1 (publish) + 10 (subscriber delivery) + 1 (persistence storage) = **12 messages**. - -If you persist the last message only, Ably stores the most recent message. The storage cost is 1 additional message per new "last message" stored. - -Retrieving persisted messages via the [history API](/docs/storage-history/history) also counts: each message returned in a history response = 1 additional message. - -To avoid additional costs for messages that do not need persistence, history, rewind, integrations, or resume, mark them as [ephemeral](/docs/pub-sub/advanced#ephemeral). Ephemeral messages are exempt from all of these features, reducing your billable message count. - -## Presence event billing - -Ably bills presence events (enter, leave, update) as regular messages: - -* 1 presence enter event with 100 presence subscribers = **101 messages** (1 enter + 100 deliveries) - -In high-churn scenarios, presence events can generate significant message volume due to the n-squared pattern. When both subscribers and members are present on a channel, each member event is delivered to every subscriber. - -For example, 200 members joining and leaving a channel generates approximately **80,400 messages**. See the [large-scale presence sets](/docs/presence-occupancy/presence#large-presence) section for the full calculation. - -To reduce presence costs: - -* Use [occupancy](/docs/presence-occupancy/occupancy) if you only need member counts, not individual identities. -* 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/platform/pricing/message-counting.mdx b/src/pages/docs/platform/pricing/message-counting.mdx new file mode 100644 index 0000000000..86b1642648 --- /dev/null +++ b/src/pages/docs/platform/pricing/message-counting.mdx @@ -0,0 +1,156 @@ +--- +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) does not reduce your message count. Ably counts all messages delivered to a connection, regardless of whether the client filters them. + +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 | +| [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/pricing#livemap-operations) set or remove | 1 inbound message per operation | +| [LiveMap](/docs/liveobjects/pricing#livemap-operations) create (shallow) | 2 inbound messages (create + assign) | +| [LiveCounter](/docs/liveobjects/pricing#livecounter-operations) increment or decrement | 1 inbound message | +| [LiveCounter](/docs/liveobjects/pricing#livecounter-operations) 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/pricing#livemap-operations) operation | 1 message per operation in the batch | + +Nested object creation generates additional messages for each nested object. See the [LiveObjects pricing](/docs/liveobjects/pricing) page for detailed examples. + +## 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). + +## 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 | 1 inbound message per published message | +| 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 [message-per-response](/docs/ai-transport/token-streaming/message-per-response), [append rollup](/docs/ai-transport/token-streaming/token-rate-limits#per-response) conflates multiple tokens into fewer inbound messages. For example, 300 tokens published individually can be conflated to approximately 100 inbound messages. See the [AI support chatbot pricing example](/docs/platform/pricing/examples/ai-chatbot) for a full breakdown. + +## 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). From a3095e98d908b6065a2eb4f02b9626aa7fef2558 Mon Sep 17 00:00:00 2001 From: umair Date: Thu, 12 Mar 2026 11:54:42 +0000 Subject: [PATCH 5/7] Add product-specific pricing pages for Spaces and LiveObjects --- src/pages/docs/liveobjects/index.mdx | 2 +- .../{concepts/billing.mdx => pricing.mdx} | 46 ++++++++++------- src/pages/docs/spaces/pricing.mdx | 50 +++++++++++++++++++ 3 files changed, 80 insertions(+), 18 deletions(-) rename src/pages/docs/liveobjects/{concepts/billing.mdx => pricing.mdx} (68%) create mode 100644 src/pages/docs/spaces/pricing.mdx diff --git a/src/pages/docs/liveobjects/index.mdx b/src/pages/docs/liveobjects/index.mdx index 999df0e6aa..929e3aef74 100644 --- a/src/pages/docs/liveobjects/index.mdx +++ b/src/pages/docs/liveobjects/index.mdx @@ -96,7 +96,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/concepts/billing.mdx b/src/pages/docs/liveobjects/pricing.mdx similarity index 68% rename from src/pages/docs/liveobjects/concepts/billing.mdx rename to src/pages/docs/liveobjects/pricing.mdx index a4d0420107..e8e7f9aea3 100644 --- a/src/pages/docs/liveobjects/concepts/billing.mdx +++ b/src/pages/docs/liveobjects/pricing.mdx @@ -1,11 +1,15 @@ --- -title: Billing -meta_description: "Understand how LiveObjects operations contribute to your Ably usage and billing." +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." +redirect_from: + - /docs/liveobjects/concepts/billing --- @@ -25,26 +29,38 @@ meta_description: "Understand how LiveObjects operations contribute to your Ably -LiveObjects operations are billed as messages. This page explains how different LiveObjects operations contribute to your Ably usage. +LiveObjects operations are billed using ObjectMessages. Each ObjectMessage follows the standard inbound/outbound counting pattern. -## Message counting +## LiveObjects operations -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. +The following table shows how LiveObjects operations contribute to your message count: -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. +| Operation | Messages counted | +| --- | --- | +| [LiveMap](#livemap-operations) set or remove | 1 inbound message per operation | +| [LiveMap](#livemap-operations) create (shallow) | 2 inbound messages (create + assign) | +| [LiveCounter](#livecounter-operations) increment or decrement | 1 inbound message | +| [LiveCounter](#livecounter-operations) 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](#livemap-operations) operation | 1 message per operation in the batch | -For example, if 5 clients are connected to a channel and one client increments a counter: +Nested object creation generates additional messages for each nested object. See the [examples below](#livemap-operations) for details. -- 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) +## 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. -## LiveMap operations +## LiveMap operation examples Removing a key and setting a primitive value always sends one message: @@ -100,7 +116,7 @@ await myObject.get('settings').batch((ctx) => { ``` -## LiveCounter operations +## LiveCounter operation examples Incrementing and decrementing a counter always sends one message: @@ -165,10 +181,6 @@ Similarly, if a client becomes disconnected and needs to resynchronize, it will 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. diff --git a/src/pages/docs/spaces/pricing.mdx b/src/pages/docs/spaces/pricing.mdx new file mode 100644 index 0000000000..fdabb494fa --- /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, consider using [occupancy](/docs/presence-occupancy/occupancy) on the underlying channel instead of tracking individual presence events. From 77a4251f01ffc4a91a81e111dd278d5474f311d0 Mon Sep 17 00:00:00 2001 From: umair Date: Thu, 12 Mar 2026 11:54:45 +0000 Subject: [PATCH 6/7] Expand Chat and Pub/Sub pricing pages with operations tables and optimization strategies --- src/pages/docs/chat/pricing.mdx | 70 ++++++++++++++++++++-------- src/pages/docs/pub-sub/pricing.mdx | 73 ++++++++++++++++++++++-------- 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/src/pages/docs/chat/pricing.mdx b/src/pages/docs/chat/pricing.mdx index 8a0004cad2..3d9c3af250 100644 --- a/src/pages/docs/chat/pricing.mdx +++ b/src/pages/docs/chat/pricing.mdx @@ -1,39 +1,69 @@ --- -title: "Pricing" +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 billing, message counting, typing indicators, reactions, chat pricing, cost optimization" +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." redirect_from: - /docs/chat/message-billing --- -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 billing rules. +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. -## What counts as a message +## Chat operations -Each Chat feature generates Pub/Sub messages. For example, 1 message sent in a room with 50 subscribers counts as **51 messages** (1 inbound + 50 outbound). Typing indicators are the most significant cost driver. In a room with 50 subscribers, one active typer generates approximately **306 messages per minute** from heartbeat events alone, plus a stop event. +The following table shows how Chat operations contribute to your message count: -For the complete breakdown of how each Chat operation counts as messages, see the [Chat operations](/docs/platform/pricing/message-billing#chat-operations) table in the central message billing reference. +| 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 | -## Channels and connection minutes +## Rooms, channels, and connections -Chat rooms map directly to Pub/Sub channels. Each room feature (messages, typing, reactions, presence, occupancy) uses underlying Pub/Sub channels. Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections) in the same way as Pub/Sub. +Each Chat room maps to underlying Pub/Sub channels. Each room feature (messages, typing, reactions, presence, occupancy) uses its own channel, contributing to your [channel count](/docs/platform/pricing#channels). Ably bills each connected client for [connection minutes](/docs/platform/pricing#connections) in the same way as Pub/Sub. -## Cost optimization strategies +## Cost optimization -The following strategies reduce Chat message costs: +### Reduce typing indicator frequency -| Strategy | How it reduces costs | -| --- | --- | -| Increase `heartbeatThrottleMs` | Reduces typing indicator event frequency | -| Disable typing indicators | Eliminates typing indicator messages in rooms where they are not needed | -| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | -| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out when you only need member counts | +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. - +### Disable typing indicators + +Disable typing indicators entirely in rooms where they are not needed. This eliminates a significant source of messages, especially in rooms with many participants. + +### 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 rooms where slight delay is acceptable. + +### 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. + +### Enable server-side presence batching + +Enable [server-side batching](/docs/presence-occupancy/presence#large-presence) to group presence events. Subscribe to presence updates only on rooms where you need member-level detail. diff --git a/src/pages/docs/pub-sub/pricing.mdx b/src/pages/docs/pub-sub/pricing.mdx index d5e773c2f6..27282326ef 100644 --- a/src/pages/docs/pub-sub/pricing.mdx +++ b/src/pages/docs/pub-sub/pricing.mdx @@ -1,41 +1,74 @@ --- -title: "Pricing" +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 billing, message counting, pricing, persistence billing, presence billing, cost optimization" +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." redirect_from: - /docs/pub-sub/message-billing --- -Pub/Sub operations contribute to your message count based on publishing, delivery, and optional features like persistence and presence. The following sections explain how Ably counts these operations and how to optimize costs. +Pub/Sub operations contribute to your message count based on publishing, delivery, and optional features like persistence and presence. -## What counts as a message +## Pub/Sub operations -Every message published through Pub/Sub counts as 1 inbound message, with each subscriber delivery counting as 1 outbound message. Optional features like persistence, history retrieval, and integrations generate additional messages. +The following table shows how Pub/Sub operations contribute to your message count: -For the complete breakdown of how each Pub/Sub operation counts as messages, see the [Pub/Sub operations](/docs/platform/pricing/message-billing#pubsub-operations) table in the central message billing reference. +| 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 | +| [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 strategies +## Cost optimization -The following table summarizes strategies for reducing message costs: +### Use ephemeral messages -| Strategy | How it reduces costs | When to use | -| --- | --- | --- | -| [Ephemeral messages](/docs/pub-sub/advanced#ephemeral) | Exempt from persistence, rewind, resume, and integrations | Streaming data where history is not needed | -| [echoMessages: false](/docs/pub-sub/advanced#echo) | Prevents self-delivery to the publisher | Optimistic UI patterns and server-side publishers | -| [Conflation](/docs/guides/pub-sub/data-streaming#solution-message-conflation) | Delivers only the latest message per key in each time window | High-frequency updates where only the latest value matters | -| [Server-side batching](/docs/guides/pub-sub/data-streaming#solution-server-side-batching) | Groups messages into single deliveries | High-throughput channels where slight delay is acceptable | -| [Delta compression](/docs/channels/options/deltas) | Reduces payload size, lowering bandwidth costs | Large, structurally similar successive messages | -| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out | When you need member counts, not individual identities | +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. From 3119aa27ddffd241a78139343a7036258b71ea93 Mon Sep 17 00:00:00 2001 From: umair Date: Thu, 12 Mar 2026 11:54:49 +0000 Subject: [PATCH 7/7] Update navigation to include product pricing pages --- src/data/nav/chat.ts | 2 +- src/data/nav/liveobjects.ts | 9 ++++----- src/data/nav/platform.ts | 4 ++-- src/data/nav/pubsub.ts | 2 +- src/data/nav/spaces.ts | 4 ++++ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/data/nav/chat.ts b/src/data/nav/chat.ts index ecb8f9abab..b8e48f3013 100644 --- a/src/data/nav/chat.ts +++ b/src/data/nav/chat.ts @@ -224,7 +224,7 @@ export default { ], }, { - name: 'Pricing', + name: 'Chat pricing', link: '/docs/chat/pricing', }, { diff --git a/src/data/nav/liveobjects.ts b/src/data/nav/liveobjects.ts index 11e38a69e2..215ea8497d 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'], - }, ], }, { @@ -104,6 +99,10 @@ export default { }, ], }, + { + name: 'LiveObjects pricing', + link: '/docs/liveobjects/pricing', + }, ], api: [ { diff --git a/src/data/nav/platform.ts b/src/data/nav/platform.ts index c37ed69151..a5070715b1 100644 --- a/src/data/nav/platform.ts +++ b/src/data/nav/platform.ts @@ -126,8 +126,8 @@ export default { name: 'Billing', }, { - link: '/docs/platform/pricing/message-billing', - name: 'Message billing', + link: '/docs/platform/pricing/message-counting', + name: 'Message counting', }, { link: '/docs/platform/pricing/limits', diff --git a/src/data/nav/pubsub.ts b/src/data/nav/pubsub.ts index 5b56d5c5be..ecf3d63a1f 100644 --- a/src/data/nav/pubsub.ts +++ b/src/data/nav/pubsub.ts @@ -357,7 +357,7 @@ export default { ], }, { - name: 'Pricing', + name: 'Pub/Sub pricing', link: '/docs/pub-sub/pricing', }, { 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: [ {