Exchange Wire Formats

OKX Spot wire format

How OKX spot (SPOT) messages become rows in our Parquet files. Each data type below shows a real message next to the rows we publish for it, with the source and the rule for every column. Hover a line or a column to see how they connect.

Checked against our pipeline

These messages were captured from OKX spot (SPOT) on 2026-10-05. Our test suite replays each one through the production collector parser and postprocessor, and fails if the published rows differ from the rows on this page. Files written before a change to the pipeline can differ; see Known Gaps & Corrections.

Every channel is subscribed per instrument with {"channel": …, "instId": "BTC-USDT"}. OKX Spot uses the same message formats and postprocessor as OKX Futures; spot sizes are in the base asset.

  • Public WebSocket (books, tickers): wss://ws.okx.com:8443/ws/v5/public
  • Business WebSocket (trades-all): wss://ws.okx.com:8443/ws/v5/business

Exchange API reference: www.okx.com. Conventions shared by every exchange are on the wire formats overview.

Trades

One row per fill from the trades-all channel.

  • WebSocket trades-all

Every trades-all message carries exactly one fill.

WebSocket message · trades-allreceived 2026-10-05 19:14:59.976916 UTC
{
"arg": {
"channel": "trades-all",
"instId": "BTC-USDT"
},
"data": [
{
"instId": "BTC-USDT",
"tradeId": "1066842395",
"px": "85668.8",
"sz": "0.00015743",
"side": "buy",
"ts": "1791227699299",
"source": "0"
}
]
}

Published row

received_time1791227699976916000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by OKX.
event_time1791227699299data[].tsCopied from the fill time. Milliseconds.
symbolBTC-USDTdata[].instIdCopied.
trade_id1066842395data[].tradeIdParsed from OKX's string to an integer.
price85668.8data[].pxCopied as the exact decimal string OKX sent.
quantity0.00015743data[].szCopied as the exact decimal string. Base asset units.
trade_time1791227699299data[].tsCopied. Milliseconds. Same value as event_time.
is_buyer_makerfalsedata[].sidetrue when side is sell (the taker sold, so the buyer was the maker); false when side is buy.
order_typeMARKET—Always MARKET. A placeholder: OKX does not send an order type.

Notes

  • Rows come from the per-fill trades-all channel on the business endpoint. The public trades channel sends one entry per taker order, fill price and order source; we stopped using it in the 2026-09 data-integrity release (see the venue notes).
  • Rows in a file are sorted by trade_time, then trade_id. A trade_id is published once per hour file.
  • OKX's source field (0 normal order, 1 Enhanced Liquidity Program order) is not published.

Column types and descriptions: Trades schema.

Order Book

One row per price level from the 400-level books channel: a snapshot on subscribe, then 100 ms updates.

  • WebSocket books

Order book snapshot (action: snapshot)

OKX sends a 400-level snapshot right after subscribing. It anchors the update chain: its prevSeqId is always -1.

Abridged: OKX sent 400 bid and 400 ask levels in this message; the best three of each are shown.

WebSocket message · booksreceived 2026-10-05 19:15:00.149844 UTC
{
"arg": {
"channel": "books",
"instId": "BTC-USDT"
},
"action": "snapshot",
"data": [
{
"asks": [
["85668.8", "0.60167743", "0", "24"],
["85669.8", "0.04202294", "0", "1"],
["85670.7", "0.19024141", "0", "1"]
],
"bids": [
["85668.7", "0.15616065", "0", "6"],
["85667.8", "0.0000102", "0", "1"],
["85667.4", "0.00001172", "0", "1"]
],
"ts": "1791227700007",
"checksum": 0,
"seqId": 82018808615,
"prevSeqId": -1
}
]
}

Published rows · 6

received_time1791227700149844000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by OKX.
event_time1791227700007data[].tsCopied from the book generation time. Milliseconds.
transaction_timenull—Null. OKX sends one book timestamp only.
symbolBTC-USDTarg.instIdCopied.
event_typesnapshotactionsnapshot when action is snapshot.
first_update_idnull—Null. OKX sends no first update ID.
final_update_id82018808615data[].seqIdCopied from seqId.
prev_final_update_idnull—Null for snapshots. A snapshot starts a new chain.
last_update_id-1data[].prevSeqIdCopied from prevSeqId, which is always -1 for a snapshot. Kept for files written before prev_final_update_id existed.
sidebiddata[].bidsdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85668.7data[].bids[][0]data[].asks[][0]Copied as the exact decimal string.
quantity0.15616065data[].bids[][1]data[].asks[][1]Copied as the exact decimal string. Base asset units.
order_count6data[].bids[][3]data[].asks[][3]Copied from the fourth level element (number of orders), as an integer. The third element is deprecated (always 0) and not published.

Order book update (action: update)

Incremental changes, pushed every 100 ms while the book changes. Each update's prevSeqId equals the previous message's seqId.

Abridged: OKX sent 11 bid and 6 ask levels in this message; two bids and two asks are shown, including one level removed with quantity 0.

WebSocket message · booksreceived 2026-10-05 19:15:00.247388 UTC
{
"arg": {
"channel": "books",
"instId": "BTC-USDT"
},
"action": "update",
"data": [
{
"asks": [
["85670.3", "0.0000102", "0", "1"],
["85755.7", "0", "0", "0"]
],
"bids": [
["85659.9", "0.06542374", "0", "3"],
["85657", "0.00448811", "0", "2"]
],
"ts": "1791227700107",
"checksum": 0,
"seqId": 82018808663,
"prevSeqId": 82018808615
}
]
}

Published rows · 4

received_time1791227700247388000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by OKX.
event_time1791227700107data[].tsCopied from the book generation time. Milliseconds.
transaction_timenull—Null. OKX sends one book timestamp only.
symbolBTC-USDTarg.instIdCopied.
event_typeupdateactionupdate when action is update.
first_update_idnull—Null. OKX sends no first update ID.
final_update_id82018808663data[].seqIdCopied from seqId.
prev_final_update_id82018808615data[].prevSeqIdCopied from prevSeqId. Equals the previous message's final_update_id; a mismatch is a gap.
last_update_id82018808615data[].prevSeqIdCopied from prevSeqId, the same value as prev_final_update_id. Kept for files written before prev_final_update_id existed.
sidebiddata[].bidsdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85659.9data[].bids[][0]data[].asks[][0]Copied as the exact decimal string.
quantity0.06542374data[].bids[][1]data[].asks[][1]Copied as the exact decimal string: the new total at that price. 0 removes the level. Base asset units.
order_count3data[].bids[][3]data[].asks[][3]Copied from the fourth level element (number of orders), as an integer.

Snapshot of an empty book

A snapshot with no levels on either side, captured on the quiet AUDF-USDT pair. It still anchors the sequence chain, so it is published as one marker row with side noop. An update with no levels whose seqId differs from prevSeqId is published the same way, as an update marker row.

WebSocket message · booksreceived 2026-10-05 19:15:41.842255 UTC
{
"arg": {
"channel": "books",
"instId": "AUDF-USDT"
},
"action": "snapshot",
"data": [
{
"asks": [],
"bids": [],
"ts": "1791227741701",
"checksum": 0,
"seqId": 696923,
"prevSeqId": -1
}
]
}

Published row

received_time1791227741842255000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by OKX.
event_time1791227741701data[].tsCopied. Milliseconds.
transaction_timenull—Null. OKX sends one book timestamp only.
symbolAUDF-USDTarg.instIdCopied.
event_typesnapshotactionsnapshot when action is snapshot.
first_update_idnull—Null. OKX sends no first update ID.
final_update_id696923data[].seqIdCopied from seqId.
prev_final_update_idnull—Null for snapshots.
last_update_id-1data[].prevSeqIdCopied from prevSeqId (-1 for a snapshot).
sidenoop—noop: the message has no levels, so one marker row keeps its sequence ID.
price0—0 on the marker row.
quantity0—0 on the marker row.
order_countnull—Null on the marker row.

Keepalive update (not published)

When a book has not changed for about 60 seconds, OKX sends an update with no levels and seqId equal to prevSeqId. It changes nothing and is not published. Captured on AUDF-USDT; the same applies to swaps.

WebSocket message · booksreceived 2026-10-05 19:16:00.142279 UTC
{
"arg": {
"channel": "books",
"instId": "AUDF-USDT"
},
"action": "update",
"data": [
{
"asks": [],
"bids": [],
"ts": "1791227760001",
"checksum": 0,
"seqId": 696923,
"prevSeqId": 696923
}
]
}

Not published

received_time—Not published: the message has no levels and does not advance the sequence.
event_time—Not published: the message has no levels and does not advance the sequence.
transaction_time—Not published: the message has no levels and does not advance the sequence.
symbol—Not published: the message has no levels and does not advance the sequence.
event_type—Not published: the message has no levels and does not advance the sequence.
first_update_id—Not published: the message has no levels and does not advance the sequence.
final_update_id—Not published: the message has no levels and does not advance the sequence.
prev_final_update_id—Not published: the message has no levels and does not advance the sequence.
last_update_id—Not published: the message has no levels and does not advance the sequence.
side—Not published: the message has no levels and does not advance the sequence.
price—Not published: the message has no levels and does not advance the sequence.
quantity—Not published: the message has no levels and does not advance the sequence.
order_count—Not published: the message has no levels and does not advance the sequence.

Notes

  • OKX retired the books checksum in 2026: the field is still sent but is always 0 and is not published. seqId/prevSeqId is the only continuity check: an update continues the book when its prev_final_update_id equals the previous message's final_update_id. All rows of one message carry the same two IDs, so check once per message, not per row.
  • OKX documents that seqId can restart at a lower value after maintenance. prevSeqId still equals the previous message's seqId, so check the link, not that seqId increases.
  • The collector validates the chain before it writes. After a gap it reconnects and starts again from a new WebSocket snapshot.
  • Quantities are in the base asset.
  • Before continuous trading starts on a new listing (OKX openType call_auction or pre_quote), the best ask can be below the best bid. The collector accepts a crossed book only in that window, from the instrument's listTime to contTdSwTime (OKX GET /api/v5/public/instruments).
  • Files can start with a checkpoint snapshot of the book carried over from the previous hour: its received_time is the hour start, transaction_time is null, and final_update_id and last_update_id are both the last seqId applied.

Column types and descriptions: Order Book schema.

Ticker

One row per tickers message.

  • WebSocket tickers
WebSocket message · tickersreceived 2026-10-05 19:15:00.014684 UTC
{
"arg": {
"channel": "tickers",
"instId": "BTC-USDT"
},
"data": [
{
"instType": "SPOT",
"instId": "BTC-USDT",
"last": "85668.8",
"lastSz": "0.00047965",
"askPx": "85668.8",
"askSz": "0.60167743",
"bidPx": "85668.7",
"bidSz": "0.15659044",
"open24h": "85389.7",
"high24h": "86994.3",
"low24h": "84979.5",
"sodUtc0": "86522.4",
"sodUtc8": "85267.6",
"volCcy24h": "557320377.998248583",
"vol24h": "6475.49027556",
"ts": "1791227699768"
}
]
}

Published row

received_time1791227700014684000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by OKX.
event_time1791227699768data[].tsCopied from the ticker generation time. Milliseconds.
symbolBTC-USDTdata[].instIdCopied.
price_change279.1data[].lastdata[].open24hComputed: last − open24h, exact decimal.
price_change_percent0.32685441data[].lastdata[].open24hComputed: (last − open24h) / open24h × 100, rounded half-even to 8 decimal places. Percent.
weighted_average_pricenull—Null. OKX does not send a weighted average price.
last_price85668.8data[].lastCopied.
last_quantity0.00047965data[].lastSzCopied. Base asset units.
open_price85389.7data[].open24hCopied. Open price of the past 24 hours.
high_price86994.3data[].high24hCopied.
low_price84979.5data[].low24hCopied.
base_asset_volume6475.49027556data[].vol24hCopied from vol24h: for spot OKX gives it in the base asset.
quote_asset_volume557320377.998248583data[].volCcy24hCopied from volCcy24h: for spot OKX gives it in the quote asset.
statistics_open_timenull—Null. OKX does not send the window bounds.
statistics_close_timenull—Null. OKX does not send the window bounds.
first_trade_idnull—Null. OKX does not send trade IDs in tickers.
last_trade_idnull—Null. OKX does not send trade IDs in tickers.
total_tradesnull—Null. OKX does not send a trade count.

Notes

  • Best bid and ask (bidPx, bidSz, askPx, askSz) and the day opens sodUtc0 and sodUtc8 are not published.

Column types and descriptions: Ticker schema.

Not published

  • Mark Price: Not collected for OKX spot. Spot markets have no funding rate.
  • Open Interest: Spot markets have no open interest.
  • Liquidations: We collect liquidations for OKX perpetual swaps only.