|
| 1 | +--- |
| 2 | +title: "Message billing" |
| 3 | +meta_description: "Understand how different Ably operations contribute to your message count, including persistence, presence, Chat SDK features, and cost optimization strategies." |
| 4 | +meta_keywords: "message billing, message counting, pricing, persistence billing, presence billing, chat billing, cost optimization" |
| 5 | +--- |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | +<Aside data-type="further-reading"> |
| 10 | +For package comparison and volume discounts, see the [pricing](https://ably.com/pricing) page. For general billing information, see [billing](/docs/platform/pricing/billing). |
| 11 | +</Aside> |
| 12 | + |
| 13 | +## Basic message counting <a id="basic-counting"/> |
| 14 | + |
| 15 | +Every message sent through Ably is counted for both publishing and delivery: |
| 16 | + |
| 17 | +* 1 publish = 1 inbound message |
| 18 | +* Each subscriber delivery = 1 outbound message per subscriber |
| 19 | + |
| 20 | +For example, 1 publisher sends a message to a channel with 10 subscribers = **11 messages** (1 inbound + 10 outbound). |
| 21 | + |
| 22 | +## Message size <a id="message-size"/> |
| 23 | + |
| 24 | +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. |
| 25 | + |
| 26 | +* `name` and `clientId` are calculated as the size in bytes of their UTF-8 representation. |
| 27 | +* `data` is calculated as the size in bytes if it is binary, or its UTF-8 byte length if it is a string. |
| 28 | +* `extras` is calculated as the string length of its JSON representation. |
| 29 | + |
| 30 | +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. |
| 31 | + |
| 32 | +See [maximum message size](/docs/platform/pricing/limits#message) for the size limits per package type. |
| 33 | + |
| 34 | +## Operations and their billing impact <a id="operations"/> |
| 35 | + |
| 36 | +The following table shows how different operations contribute to your message count: |
| 37 | + |
| 38 | +| Operation | Messages counted | Notes | |
| 39 | +| --- | --- | --- | |
| 40 | +| Publish to channel | 1 inbound + 1 per subscriber outbound | Base cost for all messaging | |
| 41 | +| Persisted message storage | +1 per message stored | Enable via [channel rules](/docs/storage-history/storage) | |
| 42 | +| History retrieval | +1 per message retrieved | Each call to the [history API](/docs/storage-history/history) | |
| 43 | +| Presence enter/leave/update | 1 inbound + 1 per presence subscriber outbound | Same counting as regular messages | |
| 44 | +| Typing indicator heartbeat | 1 inbound + 1 per room member outbound | Controlled by `heartbeatThrottleMs` | |
| 45 | +| Webhook/integration delivery | +1 per outbound delivery | Per [integration](/docs/platform/integrations) target | |
| 46 | +| Rewind on attach | Rewound messages count as outbound | Up to 100 messages per [rewind](/docs/channels/options/rewind) | |
| 47 | +| Push notification | Counted separately per push platform | See [push notifications](/docs/push) | |
| 48 | + |
| 49 | +## Persistence billing <a id="persistence"/> |
| 50 | + |
| 51 | +When persistence is enabled via [channel rules](/docs/storage-history/storage), each message stored to disk counts as an additional message. |
| 52 | + |
| 53 | +If you persist all messages, a message published to 10 subscribers with persistence enabled = 1 (publish) + 10 (subscriber delivery) + 1 (persistence storage) = **12 messages**. |
| 54 | + |
| 55 | +If you persist last message only, the most recent message is stored. The storage cost is 1 additional message per new "last message" stored. |
| 56 | + |
| 57 | +Retrieving persisted messages via the [history API](/docs/storage-history/history) also counts: each message returned in a history response = 1 additional message. |
| 58 | + |
| 59 | +To avoid persistence costs on messages that don't need history, mark them as [ephemeral](/docs/pub-sub/advanced#ephemeral). |
| 60 | + |
| 61 | +## Presence event billing <a id="presence"/> |
| 62 | + |
| 63 | +Presence events (enter, leave, update) are billed as regular messages: |
| 64 | + |
| 65 | +* 1 presence enter event with 100 presence subscribers = **101 messages** (1 enter + 100 deliveries) |
| 66 | + |
| 67 | +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. |
| 68 | + |
| 69 | +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. |
| 70 | + |
| 71 | +To reduce presence costs: |
| 72 | + |
| 73 | +* Use [occupancy](/docs/presence-occupancy/occupancy) if you only need member counts, not individual identities. |
| 74 | +* Enable [server-side batching](/docs/presence-occupancy/presence#large-presence) to group presence events and support up to 20,000 members per channel. |
| 75 | +* Subscribe to presence updates only on channels where you need member-level detail. |
| 76 | + |
| 77 | +## Chat-specific billing <a id="chat"/> |
| 78 | + |
| 79 | +The [Chat SDK](/docs/chat) uses the same billing model. |
| 80 | + |
| 81 | +Each chat message published to a room = 1 inbound + 1 per subscriber outbound. |
| 82 | + |
| 83 | +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. |
| 84 | + |
| 85 | +Each reaction = 1 inbound + 1 per subscriber outbound. |
| 86 | + |
| 87 | +## Cost optimization strategies <a id="optimization"/> |
| 88 | + |
| 89 | +The following table summarizes strategies for reducing message costs: |
| 90 | + |
| 91 | +| Strategy | How it reduces costs | When to use | |
| 92 | +| --- | --- | --- | |
| 93 | +| [Ephemeral messages](/docs/pub-sub/advanced#ephemeral) | Exempt from persistence, rewind, resume, and integrations | Streaming data where history is not needed | |
| 94 | +| [echoMessages: false](/docs/pub-sub/advanced#echo) | Prevents self-delivery to the publisher | Optimistic UI patterns and server-side publishers | |
| 95 | +| [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 | |
| 96 | +| [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 | |
| 97 | +| [Delta compression](/docs/channels/options/deltas) | Reduces payload size, lowering bandwidth costs | Large, structurally similar successive messages | |
| 98 | +| [Occupancy](/docs/presence-occupancy/occupancy) instead of presence | Avoids n-squared presence event fan-out | When you need member counts, not individual identities | |
| 99 | + |
| 100 | +<Aside data-type="further-reading"> |
| 101 | +See the [pricing](https://ably.com/pricing) page for package comparison and volume discounts. |
| 102 | +</Aside> |
0 commit comments