Skip to main content
A balance.changed event carries the absolute balance of one holder for one asset after a ledger closes. Use it to keep a wallet or ledger view current without summing transfers, and to keep an Assets API snapshot up to date. Balance events are opt-in. A subscription receives them only when its event_types lists balance.changed or balance.changed.fees. Subscriptions with an empty event_types list receive decoded events only.

Subscribe

The response is the new subscription with its one-time hmac_secret, as in Your first webhook. Selectors apply to balance events as follows: You can list balance and decoded event types in one subscription, for example ["transfer", "balance.changed"].

Payload

Payload (example values)
Amounts are strings. Parse them as exact integers or decimals, never as floating-point numbers.

Semantics

  • One event per holder, asset and ledger. Several changes inside one ledger are coalesced. The event carries the value after the ledger, not a running sum.
  • Fees. An XLM change caused only by a transaction fee (or its refund) is suppressed unless the subscription lists balance.changed.fees. That option includes all balance.changed events as well.
  • Contract tokens. Custom SEP-41 tokens are included when the Assets registry recognizes their balance storage layout. Native XLM, classic assets, SAC balances and pool shares are always included. If the registry is temporarily unavailable, delivery continues for those and custom tokens resume once it is reachable again.
  • Delivery. Balance events use the same signing, retries, ordering and credits as other deliveries, and the same (network, id) deduplication. They exist only in Webhooks; the Activity API has no balance events, so after a gap re-read holdings instead (below).

Stitch with an Assets snapshot

Because each event carries an absolute balance, you can start from any holdings snapshot without a replay:
  1. Create the subscription and read its activation_checkpoint C.
  2. Read holdings from the Assets API. Its read_view.as_of_ledger is N. Use a snapshot with N >= C.
  3. Apply only events with ledger > N, keyed by (holder, asset). Ignore events with ledger <= N; the snapshot already contains them.
  4. Keep the event with the highest ledger for each (holder, asset). An older event must never overwrite a newer one, even if it arrives later.
A duplicate event is harmless: it carries the same balance. A missed event does not corrupt anything, but the balance stays stale until the next change for that holder and asset. If the subscription reports a coverage gap, read holdings again at a ledger at or after the gap’s last_ledger and continue from step 3.

Limits

One subscription can admit at most 4,096 events per ledger. Filter balance subscriptions by account or asset: a broad selector, such as native without an account or a subscription with no account or asset at all, can match thousands of holders in one busy ledger. When a ledger exceeds the limit, that subscription records a coverage gap for the ledger with reason payload_or_policy_limit. The subscription stays active and later ledgers are delivered normally. Narrow the selector, or repair the gap with a new snapshot.