OKX Futures wire format
How OKX perpetual swaps (SWAP) 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 perpetual swaps (SWAP) 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-SWAP"}, except liquidations, which use one {"channel": "liquidation-orders", "instType": "SWAP"} subscription for all swaps. Sizes in trades, order book levels, the ticker's last_quantity, open interest and liquidations are in contracts. Multiply by the instrument's ctVal (OKX GET /api/v5/public/instruments), which is in ctValCcy units: the base coin for linear swaps (0.01 BTC for BTC-USDT-SWAP), USD for inverse swaps (100 USD for BTC-USD-SWAP).
- Public WebSocket (books, tickers, mark price, funding rate, open interest, liquidations):
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.
{"arg": {"channel": "trades-all","instId": "BTC-USDT-SWAP"},"data": [{"instId": "BTC-USDT-SWAP",→ symbol"tradeId": "2975282594",→ trade_id"px": "85620.7",→ price"sz": "0.01",→ quantity"side": "sell",→ is_buyer_maker"ts": "1791227700310",→ event_time · trade_time"source": "0"}]}
Published row
ctVal (0.01 BTC for BTC-USDT-SWAP) for the base coin.event_time.true when side is sell (the taker sold, so the buyer was the maker); false when side is buy.MARKET. A placeholder: OKX does not send an order type.Notes
- Rows come from the per-fill
trades-allchannel on the business endpoint. The publictradeschannel 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, thentrade_id. Atrade_idis published once per hour file. - OKX's
sourcefield (0normal order,1Enhanced 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.
{"arg": {"channel": "books","instId": "BTC-USDT-SWAP"→ symbol},"action": "snapshot",→ event_type"data": [{"asks": [→ side["85620.8", "1969.32", "0", "91"],→ side · price · quantity · order_count["85620.9", "52.78", "0", "4"],→ side · price · quantity · order_count["85621", "0.02", "0", "2"]→ side · price · quantity · order_count],"bids": [→ side["85620.7", "741.39", "0", "21"],→ side · price · quantity · order_count["85620.6", "26.86", "0", "3"],→ side · price · quantity · order_count["85620.5", "0.64", "0", "2"]→ side · price · quantity · order_count],"ts": "1791227700007",→ event_time"checksum": 0,"seqId": 344108675409,→ final_update_id"prevSeqId": -1→ last_update_id}]}
Published rows · 6
snapshot when action is snapshot.seqId.prevSeqId, which is always -1 for a snapshot. Kept for files written before prev_final_update_id existed.bid for levels in bids, ask for levels in asks. One row per level.ctVal (0.01 BTC for BTC-USDT-SWAP) for the base coin.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 51 bid and 17 ask levels in this message; two bids and two asks are shown, including one level removed with quantity 0.
{"arg": {"channel": "books","instId": "BTC-USDT-SWAP"→ symbol},"action": "update",→ event_type"data": [{"asks": [→ side["85620.9", "52.19", "0", "3"],→ side · price · quantity · order_count["85623.7", "0", "0", "0"]→ side · price · quantity · order_count],"bids": [→ side["85620.7", "771.51", "0", "23"],→ side · price · quantity · order_count["85619.7", "0.8", "0", "3"]→ side · price · quantity · order_count],"ts": "1791227700107",→ event_time"checksum": 0,"seqId": 344108675590,→ final_update_id"prevSeqId": 344108675409→ prev_final_update_id · last_update_id}]}
Published rows · 4
update when action is update.seqId.prevSeqId. Equals the previous message's final_update_id; a mismatch is a gap.prevSeqId, the same value as prev_final_update_id. Kept for files written before prev_final_update_id existed.bid for levels in bids, ask for levels in asks. One row per level.0 removes the level. Contracts: multiply by ctVal (0.01 BTC for BTC-USDT-SWAP) for the base coin.Notes
- OKX retired the
bookschecksum in 2026: the field is still sent but is always0and is not published.seqId/prevSeqIdis the only continuity check: an update continues the book when itsprev_final_update_idequals the previous message'sfinal_update_id. All rows of one message carry the same two IDs, so check once per message, not per row. - OKX documents that
seqIdcan restart at a lower value after maintenance.prevSeqIdstill equals the previous message'sseqId, so check the link, not thatseqIdincreases. - The collector validates the chain before it writes. After a gap it reconnects and starts again from a new WebSocket snapshot.
- Quantities are in contracts. Multiply by
ctVal(0.01 BTC forBTC-USDT-SWAP) for the base coin. - When a book has not changed for about 60 seconds, OKX sends an update with no levels and
seqIdequal toprevSeqId. It is not published. The OKX Spot page shows an example. - Files can start with a checkpoint snapshot of the book carried over from the previous hour: its
received_timeis the hour start,transaction_timeis null, andfinal_update_idandlast_update_idare both the lastseqIdapplied.
Column types and descriptions: Order Book schema.
Ticker
One row per tickers message.
- WebSocket tickers
{"arg": {"channel": "tickers","instId": "BTC-USDT-SWAP"},"data": [{"instType": "SWAP","instId": "BTC-USDT-SWAP",→ symbol"last": "85620.7",→ price_change · price_change_percent · last_price"lastSz": "0.08",→ last_quantity"askPx": "85620.8","askSz": "1989.68","bidPx": "85620.7","bidSz": "664.09","open24h": "85350.7",→ price_change · price_change_percent · open_price"high24h": "86963.7",→ high_price"low24h": "84937.5",→ low_price"sodUtc0": "86484.8","sodUtc8": "85221.6","volCcy24h": "83846.1822",→ base_asset_volume"vol24h": "8384618.22","ts": "1791227699670"→ event_time}]}
Published row
last − open24h, exact decimal.last − open24h) / open24h × 100, rounded half-even to 8 decimal places. Percent.volCcy24h: for swaps OKX gives it in the base coin (BTC), for linear and inverse swaps alike.vol24h counts contracts and is not published).Notes
- For swaps
base_asset_volumeis the 24h volume in the base coin (volCcy24h) andquote_asset_volumeis null. OKX sends no quote turnover for swaps. - Best bid and ask (
bidPx,bidSz,askPx,askSz) and the day openssodUtc0andsodUtc8are not published. - Before the October 2026 pipeline update,
quote_asset_volumefor swaps heldvolCcy24h×last. Before the 2026-09 data-integrity release,base_asset_volumeheld contracts andquote_asset_volumethe base-coin volume (see the venue notes).
Column types and descriptions: Ticker schema.
Mark Price
Two channels write into one file: mark-price rows carry mark_price, funding-rate rows carry funding_rate and next_funding_time. Rows are sorted by event_time.
- WebSocket mark-price
- WebSocket funding-rate
Mark price (mark-price channel)
Pushed every 200 ms while the mark price changes, otherwise every 10 seconds. These rows carry the mark price only.
{"arg": {"channel": "mark-price","instId": "BTC-USDT-SWAP"},"data": [{"instId": "BTC-USDT-SWAP",→ symbol"instType": "SWAP","markPx": "85623.5",→ mark_price"ts": "1791227699708"→ event_time}]}
Published row
mark-price channel has no index price.funding-rate channel.Funding rate (funding-rate channel)
Pushed every 30 to 90 seconds. These rows carry the funding fields only and go into the same file as the mark price rows.
{"arg": {"channel": "funding-rate","instId": "BTC-USDT-SWAP"},"data": [{"formulaType": "withRate","fundingRate": "0.0000263029004785",→ funding_rate"fundingTime": "1791244800000",→ next_funding_time"impactValue": "20000.0000000000000000","instId": "BTC-USDT-SWAP",→ symbol"instType": "SWAP","interestRate": "0.0001000000000000","maxFundingRate": "0.00375","method": "current_period","minFundingRate": "-0.00375","nextFundingRate": "","nextFundingTime": "1791273600000","premium": "-0.0005569827183559","prevFundingTime": "1791216000000","settFundingRate": "0.0000265340358766","settState": "settled","ts": "1791227769932"→ event_time}]}
Published row
ts, the time OKX published the update. Milliseconds. If ts is missing, our receive time in milliseconds.mark-price channel.0.0000263029004785 is 0.00263 %.fundingTime: the upcoming settlement that fundingRate applies to. Milliseconds. Not nextFundingTime, which is the settlement after it.Notes
- Each row has either the mark price or the funding fields; the others are null. Forward-fill per column to get both on one row.
index_priceandestimated_settle_priceare always null for OKX.- Funding rows published before the 2026-09 data-integrity release have
event_timeset tofundingTime(see the venue notes). - OKX states that during maintenance two
mark-pricemessages can share ats, and that the later one received is authoritative. Both are published.
Column types and descriptions: Mark Price schema.
Open Interest
One row per open-interest message, about every 3 seconds while open interest changes.
- WebSocket open-interest
Pushed every 3 seconds when open interest changes.
{"arg": {"channel": "open-interest","instId": "BTC-USDT-SWAP"},"data": [{"instId": "BTC-USDT-SWAP",→ symbol"instType": "SWAP","oi": "2850839.22000000697",→ sum_open_interest"oiCcy": "28508.3922000000697","oiUsd": "2440908496.03854596776279",→ sum_open_interest_value"ts": "1791227711966"→ timestamp}]}
Published row
oi. Contracts: multiply by ctVal (0.01 BTC for BTC-USDT-SWAP) for the base coin. OKX's base-coin figure oiCcy is not published.oiUsd. USD.Notes
- Messages with the same
tsare published once, keeping the first one received. - The collector also has a REST open interest poller (
GET /api/v5/public/open-interest); its output is not published.
Column types and descriptions: Open Interest schema.
Liquidations
One row per entry in details of the liquidation-orders channel, subscribed for all swaps.
- WebSocket liquidation-orders
{"arg": {"channel": "liquidation-orders","instType": "SWAP"},"data": [{"details": [{"bkLoss": "0","bkPx": "85659.7",→ price · average_price"ccy": "","posSide": "short","side": "buy",→ side"sz": "0.06",→ quantity · last_filled_quantity · filled_quantity"ts": "1791227898140"→ event_time · trade_time}],"instFamily": "BTC-USDT","instId": "BTC-USDT-SWAP",→ symbol"instType": "SWAP","uly": "BTC-USDT"}]}
Published row
instId. The subscription covers every swap, so one frame can carry several instruments.BUY closes a liquidated short, SELL a liquidated long.MARKET. A placeholder: OKX does not send an order type.IOC. A placeholder: OKX does not send a time in force.ctVal for the base coin.bkPx. OKX documents it as the liquidation mark price: the price of the transaction with the system's liquidation account. Not a fill price on the order book.bkPx, the same value as price. Not an execution price.FILLED. A placeholder, not an execution status.sz, the same value as quantity.sz, the same value as quantity.event_time.Notes
- OKX states that this channel does not carry every liquidation on OKX.
- OKX states that liquidations come from different sources and are not necessarily in time order. They can arrive hours after
event_time, so a row can be in a later hour's file (see the venue notes). posSide,bkLossandccyare not published.
Column types and descriptions: Liquidations schema.