Why balance stream?
Mobula already offers two portfolio-centric streams:position/positionsfor mark-to-market updates with live PnL calculationsbalance(this stream) for available balances per wallet/token pair across all supported EVM chains
positions stream, but it captures any balance change (DEX swaps, direct transfers, NFT airdrops, contract sends, cross-chain bridges, etc.). It is the recommended source when you need to know whether a wallet can safely execute a trade right now.
Endpoint details
- URL:
wss://api.mobula.io - Event type:
balance - Auth: Pass your API key inside the
authorizationfield of the WebSocket message body - Scope: Every wallet/token pair you include in
itemscounts as one logical subscription
Use this stream alongside the REST portfolio routes for reconciliation. Because it is beta-only, backwards compatibility is not guaranteed yet.
Subscription payload
Send a JSON message withtype: "balance" and provide the wallets you care about:
Parameters
| Field | Type | Description |
|---|---|---|
items | Array<BalanceItem> | List of wallet/token pairs to monitor. Each entry is processed independently. |
wallet | string | Wallet address in any case. The stream stores it in checksum format automatically. |
token | string | Token contract address. Use "native" to track the chain’s native asset (internally mapped to 0xeeee...). |
blockchain | string | Mobula chain identifier (e.g., evm:1, evm:137, evm:8453). The beta release currently covers all supported EVM chains. |
subscriptionId | string (optional) | Provide one to manage the subscription yourself. If omitted, the server creates a deterministic ID. Resending the same ID with additional items will append them without duplicating existing ones. |
subscriptionTracking | boolean (optional, default false) | When true, the server acknowledges every subscribe/unsubscribe event with a friendly message. |
Initial snapshot
You immediately receive the latest on-chain balance for every requested pair. Data is fetched via the portfolio service (RPC + DB) and normalized before being sent:Update events
Every update contains a singleWalletBalanceUpdate object along with the subscriptionId. Updates are triggered by two sources:
- Curated swap feed — fast deltas for swaps already observed by Mobula (comparable to the
positionsstream). - Transfer extractor — replayed L1 transfers (including bridge contracts and custom token transfers). This guarantees coverage even when no swap occurred.
Balance fields
| Field | Type | Description |
|---|---|---|
wallet | string | Checksummed wallet address. |
token | string | "native" or token contract address. |
chainId | string | Chain identifier (evm:1, evm:8453, …). |
balance | number | Human-readable balance with decimals applied. |
rawBalance | string | Raw balance (wei-style integer). |
decimals | number | ERC-20 decimals used to derive balance. |
symbol | string | Token symbol resolved by the portfolio service. |
name | string | Token name. |
logo | string | null | CDN logo URL when available. |
previousBalance | number (update events only) | Last balance before the delta was applied. |
previousRawBalance | string (update events only) | Last raw balance. |
Data flow & latency profile
- Transfers-first: Every message ultimately comes from Mobula’s transfer extractor, so even non-market interactions (OTC transfers, airdrops, gas refunds) are reflected.
- Curated swap shortcuts: While waiting for transfer batches, swaps also emit deltas from the curated events stream to keep hot wallets responsive.
- Redis caching (10 s TTL): Prevents redundant queries when several clients watch the same item; also protects downstream RPCs.
- DB reconciliation: Transfer batches trigger batched DB reads to re-sync balances after large bursts of activity or when exotic chains (listed in
EXOTIC_CHAINS) are involved.
- Swaps: ~300–700 ms slower than the
positionsstream - Pure transfers: ~1–5 s after the block is finalized (chain-dependent)
- Backfills: The stream replays cached balances whenever you reconnect or re-send the payload.
Example implementation
Use cases
Pre-trade checks
Prevent failed swaps by verifying that a wallet still has the tokens you expect before sending the transaction.
Custody dashboards
Mirror your hot-wallet balances across chains without running your own balance indexers.
Risk controls
Trigger alerts if a whale moves funds across chains or drains a vault outside of normal trading hours.
Automation
Feed state machines or bots that must keep track of spending allowances in near real time.
Error handling & best practices
- Deterministic IDs: Store the
subscriptionIdreturned by the server and reuse it whenever you reconnect so the backend can deduplicate state. - Per-chain batching: Keep unrelated wallets in separate connections if you plan to monitor hundreds of balances to avoid hitting the 100-item soft limit per socket.
- Idempotent consumers: Updates always include the latest absolute balance. Use them as truth instead of applying your own deltas.
- Native alias: Prefer
"native"over hardcoding0xeeee...in clients—the server takes care of normalization.
Rate limits
- Per connection: Up to 100 wallet/token pairs per WebSocket (same as other indexing streams).
- Per account: Standard Mobula WebSocket rate limits apply (burst + sustained). Contact support if you need dedicated throughput for the beta.
- Payload size: Keep
itemslists under 1,000 entries to avoid server-side validation failures.