> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.immix.xyz/api-reference/market-data/aggregated-book/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.immix.xyz/_mcp/server. # Aggregated book **`Request — sent`** ```json title="Request — sent" { "op": "subscribe", "params": { "topics": [ { "channel": "agg_book", "name": "", "tier": "100ms", "sources": [ { "instrument": "" } ] } ] } } ``` **`Response — received`** ```json title="Response — received" { "op": "subscribe", "success": true, "topics": [ { "topic": "agg_book.{name}.{tier}", "state": "live" } ] } ``` **`Push data — received`** ```json title="Push data — received" { "event": "agg_book", "topic": "agg_book.{name}.{tier}", "seq": "", "seqTs": "", "ts": "", "status": "degraded", "priceScale": 0, "qtyScale": 0, "excluded": [ { "s": "", "reason": "stale" } ], "included": [ { "s": "" } ], "data": { "name": "", "bids": [ { "px": "", "qty": "", "src": [ { "s": "", "px": "", "qty": "" } ] } ], "asks": [ { "px": "", "qty": "", "src": [ { "s": "", "px": "", "qty": "" } ] } ] } } ``` Each frame is its shape, read off the contract: `` stands for a value, and a union shows its first form. **URL** `wss://marketdata.immix.xyz/` **Topic** `agg_book.{name}.{tier}` | Topic parameter | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | the client-chosen handle, 1-64 of \[A-Za-z0-9.\_-] (dots legal — the tier is the single trailing dot-separated token, parsed from the right) | | `tier` | the mandatory conflation tier, from the configured set | ## Request parameters | Parameter | Type | Required | Description | | ------------------------- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `op` | string | Yes | `subscribe` | | `reqId` | string | No | client correlation id, echoed verbatim on the ack | | `params` | object | Yes | | | > `topics` | array of objects | Yes | one composite subscription — channel is the schema-required const discriminator; the composed topic agg\_book.\{name}.\{tier} is echoed in the ack and on every frame, and is the handle unsubscribe takes | | > > `channel` | string | Yes | `agg_book` | | > > `name` | string | Yes | the client-chosen handle, scoped per session | | > > `tier` | string | Yes | The conflation tier vocabulary — derived from the gateway's configured tier set (xyz.immix.md.gateway.tiers.ms); CI validates the two agree. One of `100ms`, `250ms`, `500ms`. | | > > `maxDepth` | integer | No | merged (post-coalesce) levels served per side — not venue contributions; absent serves the deployment's configured maximum (50 by default) | | > > `replace` | boolean | No | marks an intentional reconfigure of this session's live (name, tier) — without it a differing config answers name\_in\_use | | > > `sources` | array of objects | Yes | one configured source of a composite — shared verbatim by agg\_book and vol\_book entries (the ladder prices the same adjusted merge) | | > > > `instrument` | string | Yes | refdata's canonical symbol; all sources must share one base asset, and every source's quantities must already speak that base asset — a source whose contractMultiplier states a ratio other than 1 changes the unit and is rejected (unit\_mismatch). An unstated multiplier and a multiplier of exactly 1 (one contract is one base unit) are both unit-preserving and serve normally. | | > > > `bidMarginBps` | integer | No | subtracts from bid prices, rounding down — never flattering | | > > > `askMarginBps` | integer | No | adds to ask prices, rounding up | | > > > `normalisationMode` | string | No | MID normalises the quote leg through the internal index: the source's prices multiply by the current (source-quote -> normalisedAsset) rate before margins, rounding directionally (bids down, asks up). A source whose pair has no current rate within the index's 30 s consumer window is excluded from the merge and announced (reason fx\_expired) — never mixed in raw. The touch modes (FAR\_TOUCH/NEAR\_TOUCH) are reserved vocabulary and answer bad\_normalisation. One of `NONE`, `MID`. | | > > > `normalisedAsset` | string | No | the normalisation target's asset symbol — required with MID (enforced server-side: bad\_normalisation when missing, unknown, equal to the composite's base asset, or equal to the source's own quote asset); meaningless with NONE (also bad\_normalisation). Targets are validated per source and are NOT required to agree across sources — the unit consistency of the composite (every source priced in one quote unit, normalised or native) is the subscriber's design, not checked by the gateway | | > > > `disabled` | boolean | No | excluded from the merge by configuration (never announced as degradation); still counts toward the composite's output scales, so toggling it through a replace never changes the topic's scales | ## Response parameters | Parameter | Type | Required | Description | | -------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `op` | string | Yes | One of `auth`, `subscribe`, `unsubscribe`, `error`. | | `reqId` | string | No | | | `success` | boolean | Yes | | | `error` | string | No | One of `auth_required`, `invalid_token`, `unknown_op`, `malformed`, `too_many_topics`. | | `topics` | array of objects | No | answers ride in request order — the correlator for entries whose topic could not compose (error rows echo the composed topic best-effort, absent parts empty) | | > `topic` | string | Yes | | | > `state` | string | Yes | live: data flows now (latest-image lanes — tob, mark, funding, oi, ticker — answer live whether or not an image is held yet; the first image flows on arrival). syncing (book topics): the gateway holds no synced book yet — the snapshot is pushed unprompted the moment it syncs, at latest one connector restate interval. error on a plain (string-topic) row, no code: the topic composed nothing, and it never arms retroactively. Either the channel is outside the vocabulary or the tier suffix is outside the configured set (an unrecognized suffix reads as part of the symbol) — correct the topic — or the symbol is unknown to the gateway's refdata fold (derivative symbols are refdata's ccxt spelling BASE/QUOTE:SETTLE, e.g. OKX\@BTC/USDT:USDT — there is no :SWAP form) — resubscribe once refdata holds the instrument. Composite rows: live iff every enabled source book is synced, stated within the topic's output scales, AND every required FX pair is current (the full image pushes immediately); syncing otherwise, kept by the first tick at which the composite becomes computable — a source out for precision is named by a degraded frame at that tick, since syncing alone does not say it; a composite error row carries code. One of `live`, `syncing`, `error`, `unsubscribed`, `not_subscribed`. | | > `priceScale` | integer | No | composite live/syncing rows only — the resolved output price scale (max over all configured sources' reference records, disabled included), bounding the fractional digits any price on the topic can carry; never padding. It holds until a status rescaled frame on the topic restates it | | > `qtyScale` | integer | No | composite rows only — the resolved output qty scale; as priceScale | | > `code` | string | No | composite error rows only — the closed vocabulary clients parse; every validation failure maps to exactly one code. One of `unknown_channel`, `bad_name`, `bad_tier`, `tier_required`, `name_in_use`, `unknown_instrument`, `duplicate_source`, `mixed_base_asset`, `unit_mismatch`, `bad_normalisation`, `bad_depth`, `bad_ladder`, `too_many_sources`, `budget_exceeded`, `malformed`. | | > `message` | string | No | supplementary human text naming the offending source or field — never something a client parses | ## Push data parameters | Parameter | Type | Required | Description | | ------------ | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `event` | string | Yes | `agg_book` | | `topic` | string | Yes | | | `seq` | string | Yes | the stream position at render — the global sequence of the last event dispatched before this frame rendered: provenance, not a per-topic chain (gaps between a topic's frames are expected and meaningless) | | `seqTs` | string | Yes | sequencer epoch-ns of that event, as a decimal string | | `ts` | string | No | the newest included source's exchangeTsNs, written by one rule on every form: present when an included source carries one, absent otherwise — never "0". Absent therefore on the stale marker, on a degraded frame that leaves no source included, and on the empty image of a composite whose sources are all config-disabled; and always absent on rescaled | | `status` | string | No | status frames only; data is absent. degraded, recovered and stale are source transitions; rescaled says the topic's output scales changed (a source's reference record now states another scale pair, so the gateway rebound the composite) — the pair the subscribe ack echoed no longer bounds the numbers on the topic, and the frame carries the pair that does. Every frame after it is rendered at the new pair. One of `degraded`, `recovered`, `rescaled`, `stale`. | | `priceScale` | integer | No | rescaled frames only — the topic's new output price scale: the same number the subscribe ack's priceScale carried, restated | | `qtyScale` | integer | No | rescaled frames only — the topic's new output quantity scale | | `excluded` | array of objects | No | degraded frames — the sources that just left the merge, and for precision also a source that cannot enter it (announced once, even if the topic has never shown it in src) or whose announced reason became precision | | > `s` | string | Yes | the source's refdata canonical symbol | | > `reason` | string | Yes | the closed vocabulary, complete so consumers bind once; fx\_expired = the source's MID pair has no current index rate, precision = the source's book is stated at more decimal places than the topic's output scales hold, so its levels cannot enter the merge without dropping digits (it rejoins, as recovered, once its book is within the topic's scales again — the topic was rescaled wide enough, or the venue's next snapshot states a coarser scale; a rescale that widens only part of the way leaves it out), stale = any other unservable book; delisted and disabled are reserved. One of `stale`, `fx_expired`, `precision`, `delisted`, `disabled`. | | `included` | array of objects | No | recovered frames — the sources that just rejoined | | > `s` | string | Yes | | | `data` | object | No | | | > `name` | string | Yes | the client-chosen composite name | | > `bids` | array of objects | Yes | merged levels, best (highest effective bid) first | | > > `px` | string | Yes | the effective price, at the topic's output priceScale — the ack's, or the latest rescaled frame's | | > > `qty` | string | Yes | total resting quantity across contributions, at the topic's output qtyScale | | > > `src` | array of objects | Yes | contributions in source-config order, a source's own entries best-first; usually one per venue — several when a margin collapses adjacent raw levels onto one effective price | | > > > `s` | string | Yes | the source's refdata canonical symbol | | > > > `px` | string | Yes | the venue-native raw price, as the source's book states it — shortest form, and never more fractional digits than the topic's priceScale (a source stated finer is out of the merge, reason precision) | | > > > `qty` | string | Yes | the resting quantity, as the source's book states it; bounded by the topic's qtyScale the same way | | > `asks` | array of objects | Yes | merged levels, best (lowest effective ask) first | | > > `px` | string | Yes | the effective price, at the topic's output priceScale — the ack's, or the latest rescaled frame's | | > > `qty` | string | Yes | total resting quantity across contributions, at the topic's output qtyScale | | > > `src` | array of objects | Yes | contributions in source-config order, a source's own entries best-first; usually one per venue — several when a margin collapses adjacent raw levels onto one effective price | | > > > `s` | string | Yes | the source's refdata canonical symbol | | > > > `px` | string | Yes | the venue-native raw price, as the source's book states it — shortest form, and never more fractional digits than the topic's priceScale (a source stated finer is out of the merge, reason precision) | | > > > `qty` | string | Yes | the resting quantity, as the source's book states it; bounded by the topic's qtyScale the same way | ## Behaviour Subscribed with an OBJECT entry in params.topics carrying the composition (sources with optional per-side bps margins, optional maxDepth); the per-topic ack echoes the resolved output priceScale/qtyScale, which bound every number the topic can carry. The name is the client's handle, scoped per session; config binds to (name, tier) at first subscribe — an identical re-subscribe is an idempotent re-ack, a different config without "replace": true answers name\_in\_use, and "replace": true swaps the config atomically (full validation first: a failing replace answers its error code and leaves the old config live; the subscription survives, the ack re-echoes the possibly-changed scales, the next frame renders the new config; the tier is topic identity and cannot be replaced). Frames are full images at each tick — idempotent, no chain semantics, sides always present (\[] for a one-sided market) — with merged levels as objects: the effective price that ordered the level, total quantity at the output scales, and per-venue src attribution (venue-native raw prices at each venue's own scales). Sources that cannot be served (a stale book) leave the merge and status frames announce the transitions: degraded/recovered name the sources, and when every enabled source is out the topic behaves as the book channel's stale — marker, then silence, then the healing image; between transitions the current exclusion set is always the configured sources minus those visible in src. Reasons served: fx\_expired for a MID source whose index pair lapsed the 30 s consumer window, precision for a source whose book is stated finer than the topic's output scales, stale for any other unservable-book transition; delisted is reserved vocabulary. precision is the one exclusion announced for a source that never entered the merge: a source merely not synced yet is covered by the ack's syncing promise and joins in silence, but a precision source holds a servable book and stays out until the catalog or the venue moves, so its degraded frame is sent once whether it left the merge or was never in it — and when that leaves no source included, ahead of the stale posture's silence (the frame then carries no ts, names every source that left with it, and a topic that has not served yet gets no stale marker after it: syncing still stands). precision is also the one reason said over another: a source already announced stale or fx\_expired whose exclusion turns into precision is named again, in a degraded frame carrying the new reason, because the first word promised a heal within a restate interval or a rate tick and this one waits on the catalog; no other change of reason is re-announced. The output scales are the pair the ack echoed; they can change under a live subscription when a source's reference record restates its scales, and a status rescaled frame carrying the new priceScale and qtyScale says so before the first frame rendered at them. Unsubscribe is by the topic string alone, byte-identical to the echo — the config is never re-sent. Nothing survives the socket: on reconnect, resubscribe with the full config and the ack + immediate image behave as a first subscribe. > Client-configured aggregated multi-exchange books, computed merge-on-read over the gateway's book folds and conflated at the topic's mandatory tier (composites have no raw lane).