Message types
Every message is a single JSON object with a type string. Every quote and trade frame carries its event time in ts (unix ms) on every channel; numeric field encoding still differs by asset family.
system
Control frames sent by the server in response to connection events and client actions.
event: "ready"— sent immediately after a successful upgrade. Includests(unix ms). Wait for this before sending subscribe.event: "subscribed"— echoes normalizedchannelsandsymbols.event: "unsubscribed"— echoes the removedchannelsandsymbols.event: "pong"— reply to a client JSON{ "action": "ping" }, includes serverts.event: "disconnect"— server is closing the socket (for examplecode: "HEARTBEAT_TIMEOUT"after missed native WebSocket pongs, orcode: "SERVER_RESTART"right before a release closes the stream with code1012).
{
"type": "system",
"event": "ready",
"ts": 1743512400000
}quote
Emitted on *.quotes channels. Every frame carries ts (event time, unix ms) and asset. Numeric field encoding depends on the asset family:
- Crypto, forex, stocks — numeric fields as strings (for example
"bid": "189.12"). - Indices, ETFs, commodities — numeric fields as numbers on most frames; some tick-driven ETF and index frames arrive through a faster internal path that encodes the same fields as strings. Decode these channels tolerantly (accept both; a single frame never mixes the two). These channels also carry
timestamp, a mirror oftskept for clients built against their original shape. Readtsin new code.
Size fields (bid_size, ask_size, trade size) are number | null (string-encoded on the string channels): null appears when the consolidated feed has no size at that moment.
Frames replayed from cache right after subscribe are marked "snapshot": true. You will see them on the stocks, ETFs, indices and commodities channels; crypto and forex streams are continuously live, so replayed frames rarely, if ever, occur there. See Subscriptions for the snapshot behavior, the "snapshot": false opt-out and the "snapshot": "always" opt-in that keeps a cached value while a market is closed.
{
"type": "quote",
"channel": "crypto.quotes",
"asset": "crypto",
"symbol": "BTCUSD",
"bid": "84210.12",
"ask": "84210.45",
"bid_size": "1.2",
"ask_size": "0.8",
"ts": 1743512400000
}{
"type": "quote",
"channel": "stocks.quotes",
"asset": "stocks",
"symbol": "US:AAPL",
"bid": "189.12",
"ask": "189.18",
"bid_size": "100",
"ask_size": "200",
"ts": 1743512400000
}{
"type": "quote",
"channel": "indices.quotes",
"asset": "indices",
"symbol": "US500",
"bid": 4788.12,
"ask": 4788.12,
"bid_size": 62,
"ask_size": 58,
"ts": 1743512400000,
"timestamp": 1743512400000
}{
"type": "quote",
"channel": "commodities.quotes",
"asset": "commodities",
"symbol": "XAUUSD",
"bid": 2318.1,
"ask": 2318.74,
"bid_size": 58,
"ask_size": 48,
"ts": 1743512400000,
"timestamp": 1743512400000
}Entitlements may omit a channel until coverage is enabled for the account.
trade
Emitted on *.trades channels. Same rules as quotes: ts and asset on every frame, string numerics on crypto/forex/stocks, and the legacy timestamp mirror plus mostly-number numerics (decode tolerantly) on indices/ETFs/commodities:
{
"type": "trade",
"channel": "crypto.trades",
"asset": "crypto",
"symbol": "BTCUSD",
"price": "84215.42",
"size": "0.53",
"ts": 1743512400000
}{
"type": "trade",
"channel": "etfs.trades",
"asset": "etfs",
"symbol": "US500ETF",
"price": 718.66,
"size": 90,
"ts": 1743512400000,
"timestamp": 1743512400000
}size may be omitted or null when the tape has no lot size.
rate
Emitted on bonds.quotes: the latest published government bond yield for a subscribed CC:TENOR symbol. Bond yields are daily official curve observations, not ticks, so this channel delivers the current observation as a snapshot frame right after you subscribe and then one frame per symbol whenever a new observation is published, in practice once per business day. A quiet hour on this channel is normal. Numeric fields are numbers, and the fields match GET /bond/snapshot.
{
"type": "rate",
"channel": "bonds.quotes",
"asset": "bonds",
"symbol": "US:10Y",
"rate": 4.79,
"unit": "percent",
"date": "2026-09-02",
"prev_rate": 4.76,
"prev_date": "2026-09-01",
"change": 0.03,
"change_bps": 3,
"change_percent": 0.6303,
"ts": 1788307200000,
"timestamp": 1788307200000
}tsandtimestampare the observation date (UTC midnight ofdate), never the send time. Comparedatewithprev_datefor the interval the change covers.changeis in percentage points,change_bpsin basis points,change_percentrelative toprev_rate. Theprev_*and change fields arenullwhen only one observation is available.- The replayed frame on subscribe carries
"snapshot": true; frames for a newly published observation do not.
status
Emitted on stocks.status: trading halt and resume events for US-listed equities. Subscribe with explicit symbols (US:KO) or, with the market wildcard add-on enabled on your account, US:* for every US listing. Events are delivered live from subscribe time forward; this channel never replays a snapshot, because a cached status is a claim about the present that may no longer hold.
{
"type": "status",
"channel": "stocks.status",
"asset": "stocks",
"symbol": "US:KO",
"status": "halted",
"reason": "news_pending",
"ts": 1743512400000
}statusis one of four lifecycle values:halted(trading halted),paused(volatility, limit-up/limit-down pause),quoting(quotations resumed while trading is still halted, the intermediate step before some resumptions),resumed(trading resumed).reasonis our aggregation layer's classification of why:volatility,market_wide,news_pending,news_released,information_requested,regulatory,corporate_action,operationaloripo. It is omitted when no classification applies, and new values may be added over time, so decode it tolerantly.
Both fields are derived classifications produced by our aggregation layer, consistent with the data notice that applies to every TickerLayer feed. Only halt-lifecycle events are published: auction imbalances, price indications and short sale restriction notices are not part of this channel. Coverage is US equities; other markets do not emit on this channel yet.
error
Sent when a client action fails validation or a limit is breached after the connection is open. Subscribe failures use event: "subscribe_failed" (or subscribe_partial_failed). There is no details object — rejected symbols appear in rejected when present.
{
"type": "error",
"event": "subscribe_failed",
"code": "SYMBOL_LIMIT_EXCEEDED",
"message": "rate limit exceeded"
}{
"type": "error",
"event": "subscribe_failed",
"code": "INVALID_SYMBOL",
"message": "invalid symbol",
"channel": "stocks.quotes",
"rejected": [
{ "symbol": "NOTREAL", "reason": "unknown" }
]
}For the full list of error codes, see the Errors & troubleshooting page.