Bitget Futures wire format
How Bitget USDT-M, USDC-M and Coin-M Futures 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 Bitget USDT-M, USDC-M and Coin-M Futures 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.
The collector subscribes to the books, trade and ticker channels for every tradable contract of the USDT-FUTURES, USDC-FUTURES (for example BTCPERP) and COIN-FUTURES product types, and to the liquidation topic once per product type. The examples use BTCUSDT. There are no REST polls: open interest and mark prices come from the ticker channel.
- Public WebSocket V2 (order book, trades, ticker):
wss://ws.bitget.com/v2/ws/public - Public WebSocket V3 (liquidations):
wss://ws.bitget.com/v3/ws/public
Exchange API reference: www.bitget.com. Conventions shared by every exchange are on the wire formats overview.
Trades
One row per trade from the trade channel.
- WebSocket trade
Trade snapshot on subscribe
Right after subscribing, Bitget resends recent trades, newest first, as one snapshot message. They are published like any other trade, sorted oldest first; trades already published in an earlier hour, or earlier in the same hour, are dropped.
Abridged: The snapshot carried the 50 most recent trades; the newest three are shown.
{"action": "snapshot","arg": {"instType": "USDT-FUTURES","channel": "trade","instId": "BTCUSDT"→ symbol},"data": [{"ts": "1791227481512",→ event_time · trade_time"price": "85599.9",→ price"size": "0.2581",→ quantity"side": "buy",→ is_buyer_maker"tradeId": "1491081618619383808"→ trade_id},{"ts": "1791227480460",→ event_time · trade_time"price": "85599.9",→ price"size": "0.209",→ quantity"side": "buy",→ is_buyer_maker"tradeId": "1491081614206976018"→ trade_id},{"ts": "1791227480460",→ event_time · trade_time"price": "85599.9",→ price"size": "0.0001",→ quantity"side": "buy",→ is_buyer_maker"tradeId": "1491081614206976016"→ trade_id}],"ts": 1791227482003}
Published rows · 3
data[].ts, not the envelope ts. Milliseconds.event_time.true when the taker side side is sell, false when it is buy.MARKET. Bitget does not send an order type; this is a placeholder.Trade update
Later messages carry new trades only. One message can hold several trades, newest first; each becomes its own row.
{"action": "update","arg": {"instType": "USDT-FUTURES","channel": "trade","instId": "BTCUSDT"→ symbol},"data": [{"ts": "1791227487186",→ event_time · trade_time"price": "85599.8",→ price"size": "0.0027",→ quantity"side": "sell",→ is_buyer_maker"tradeId": "1491081642417864704"→ trade_id}],"ts": 1791227487186}
Published row
data[].ts, not the envelope ts. Milliseconds.event_time.true when the taker side side is sell, false when it is buy.MARKET. Bitget does not send an order type; this is a placeholder.Notes
- Bitget's
sideis the taker's side. Bitget sends no order type, soorder_typeis alwaysMARKET; useis_buyer_makerfor the aggressor side. - Trade IDs increase over time but are not consecutive. Do not use gaps in
trade_idto detect missed trades.
Column types and descriptions: Trades schema.
Order Book
One row per price level from the books channel: a snapshot on subscribe, then incremental updates.
- WebSocket books
Book snapshot
The first books message after subscribing (and after every reconnect) is a snapshot of the book. Each level becomes one snapshot row.
Abridged: Bitget sent 500 bid and 500 ask levels in this snapshot; the best three of each are shown.
{"action": "snapshot",→ event_type"arg": {"instType": "USDT-FUTURES","channel": "books","instId": "BTCUSDT"→ symbol},"data": [{"asks": [→ side["85599.9", "0.5811"],→ side · price · quantity["85600", "0.1907"],→ side · price · quantity["85601.9", "0.0002"]→ side · price · quantity],"bids": [→ side["85599.8", "3.4111"],→ side · price · quantity["85599.7", "0.0598"],→ side · price · quantity["85599.2", "0.2628"]→ side · price · quantity],"ts": "1791227482002",→ transaction_time"seq": 1062970931190,→ final_update_id"pseq": 0}],"ts": 1791227482021→ event_time}
Published rows · 6
ts, Bitget's push time. Milliseconds.data[].ts, the matching-engine time. Milliseconds.snapshot when action is snapshot.seq chain. Bitget sends pseq 0 on snapshots.bid for levels in bids, ask for levels in asks. One row per level.books channel does not report order counts.Book update
The first update after the snapshot above. Its pseq equals the snapshot's seq, so the sequence chain is unbroken.
Abridged: Bitget sent 8 bid and 17 ask levels in this update; the first 2 bid and the first 3 ask levels are shown.
{"action": "update",→ event_type"arg": {"instType": "USDT-FUTURES","channel": "books","instId": "BTCUSDT"→ symbol},"data": [{"asks": [→ side["85602.2", "0"],→ side · price · quantity["85603.7", "0.0001"],→ side · price · quantity["85603.8", "0.0808"]→ side · price · quantity],"bids": [→ side["85599.8", "3.8911"],→ side · price · quantity["85599.7", "0.0597"]→ side · price · quantity],"ts": "1791227482100",→ transaction_time"seq": 1062970936888,→ final_update_id"pseq": 1062970931190→ prev_final_update_id}],"ts": 1791227482103→ event_time}
Published rows · 5
ts, Bitget's push time. Milliseconds.data[].ts, the matching-engine time. Milliseconds.update when action is update.seq of the previous push on this subscription; a mismatch with the previous message's final_update_id is a gap. All rows of one message share it, so check once per message, not per row.bid for levels in bids, ask for levels in asks. One row per level.0 removes the level.books channel does not report order counts.Notes
transaction_timeholds the matching-engine time from the October 2026 pipeline update; it is null in files written before it.event_timehas always been the push time.- Each update's
prev_final_update_id(pseq) equals the previous message'sfinal_update_id(seq). A mismatch means a lost message; resume from the next snapshot. - A message with no levels at all is published as one row with
sidenoop,price0andquantity0, so theseqchain stays complete. Anoopupdate changes no level. Anoopsnapshot means the book is empty: clear your book. - 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_idboth hold theseqof the last message applied. The next update'sprev_final_update_idlinks to it.
Column types and descriptions: Order Book schema.
Ticker
One row per ticker push.
- WebSocket ticker
{"action": "snapshot","arg": {"instType": "USDT-FUTURES","channel": "ticker","instId": "BTCUSDT"},"data": [{"instId": "BTCUSDT",→ symbol"lastPr": "85599.9",→ last_price"bidPr": "85599.8","askPr": "85599.9","bidSz": "4.0378","askSz": "0.4618","open24h": "85345.7",→ open_price"high24h": "86966.9",→ high_price"low24h": "84929.5",→ low_price"change24h": "0.00298",→ price_change_percent"fundingRate": "0.000063","nextFundingTime": "1791244800000","markPrice": "85599.9","indexPrice": "85636.34","holdingAmount": "31506.4859999999786","baseVolume": "41733.57",→ base_asset_volume"quoteVolume": "3588373746.3737",→ quote_asset_volume"openUtc": "86473.2","symbolType": "1","symbol": "BTCUSDT","ts": "1791227481990"}],"ts": 1791227481991→ event_time}
Published row
ts, Bitget's push time. Milliseconds.change24h multiplied by 100. Bitget sends a fraction (0.01 is 1 %); the column is in percent.Notes
price_change_percentis in percent from the October 2026 pipeline update. Files written before it hold Bitget's fraction unchanged (0.01for 1 %); multiply those values by 100.- The ticker's best bid and ask (
bidPr,askPr,bidSz,askSz) andopenUtcare not published. Mark price, index price and funding go tomark_price; open interest goes toopen_interest.
Column types and descriptions: Ticker schema.
Mark Price
One row per futures ticker push, from the ticker's mark price, index price and funding fields. The collector subscribes to no separate mark price channel.
- WebSocket ticker
The same ticker message as on the ticker page. Every futures ticker push also becomes one mark price row.
{"action": "snapshot","arg": {"instType": "USDT-FUTURES","channel": "ticker","instId": "BTCUSDT"},"data": [{"instId": "BTCUSDT",→ symbol"lastPr": "85599.9","bidPr": "85599.8","askPr": "85599.9","bidSz": "4.0378","askSz": "0.4618","open24h": "85345.7","high24h": "86966.9","low24h": "84929.5","change24h": "0.00298","fundingRate": "0.000063",→ funding_rate"nextFundingTime": "1791244800000",→ next_funding_time"markPrice": "85599.9",→ mark_price"indexPrice": "85636.34",→ index_price"holdingAmount": "31506.4859999999786","baseVolume": "41733.57","quoteVolume": "3588373746.3737","openUtc": "86473.2","symbolType": "1","symbol": "BTCUSDT","ts": "1791227481990"}],"ts": 1791227481991→ event_time}
Published row
ts, Bitget's push time. Milliseconds.deliveryPrice when the push has it. Bitget documents it for delivery contracts, pushed from one hour before delivery. Null here: this perpetual ticker has no deliveryPrice field.0.000063 is 0.0063 %.Column types and descriptions: Mark Price schema.
Open Interest
One row per ticker push, from the ticker's holdingAmount. There is no REST poll.
- WebSocket ticker
The same ticker message again. Every futures ticker push also becomes one open interest row.
{"action": "snapshot","arg": {"instType": "USDT-FUTURES","channel": "ticker","instId": "BTCUSDT"},"data": [{"instId": "BTCUSDT",→ symbol"lastPr": "85599.9","bidPr": "85599.8","askPr": "85599.9","bidSz": "4.0378","askSz": "0.4618","open24h": "85345.7","high24h": "86966.9","low24h": "84929.5","change24h": "0.00298","fundingRate": "0.000063","nextFundingTime": "1791244800000","markPrice": "85599.9",→ sum_open_interest_value"indexPrice": "85636.34","holdingAmount": "31506.4859999999786",→ sum_open_interest · sum_open_interest_value"baseVolume": "41733.57","quoteVolume": "3588373746.3737","openUtc": "86473.2","symbolType": "1","symbol": "BTCUSDT","ts": "1791227481990"→ timestamp}],"ts": 1791227481991}
Published row
…9999786). Base asset units.holdingAmount × markPrice, computed in exact decimal and rounded to 8 decimal places. Quote currency (USDT for USDT-margined contracts, USDC for USDC-margined contracts, USD for coin-margined contracts). Null if either field is missing.data[].ts, Bitget's data time. Milliseconds.Notes
- Rows repeat at the ticker's rate even when open interest has not changed.
sum_open_interest_valuewas empty before the 2026-09 data-integrity release.
Column types and descriptions: Open Interest schema.
Liquidations
One row per entry of the V3 liquidation topic, subscribed once per product type (usdt-futures, usdc-futures, coin-futures).
- WebSocket liquidation
No BTCUSDT liquidation arrived while we captured, so this example is for ZECUSDT. The sell side means a short position was liquidated.
{"action": "update","arg": {"instType": "usdt-futures","topic": "liquidation"},"data": [{"symbol": "ZECUSDT",→ symbol"side": "sell",→ side"price": "1348.598068598069",→ quantity · price · average_price · last_filled_quantity · filled_quantity"amount": "775.443889443889",→ quantity · last_filled_quantity · filled_quantity"ts": "1791227617704"→ event_time · trade_time}],"ts": 1791227617724}
Published row
data[].ts. Milliseconds.buy (a long position was liquidated) becomes SELL, sell (a short position was liquidated) becomes BUY.Market. A placeholder: Bitget sends no order type.IOC. A placeholder: Bitget sends no time in force.amount ÷ price, computed in exact decimal and rounded to 6 decimal places. Base asset units. Bitget sends amount in quote currency.price. Bitget sends no average fill price.FILLED. A placeholder: Bitget sends no order status.quantity.quantity.event_time.Notes
- Bitget pushes liquidations once per second and sends at most one record per symbol and position side per second: the largest. Rows are an exchange-sampled subset, not every liquidation, so counts and totals are a lower bound.
- An entry without a positive
priceandamountis not published, because it has no valid base quantity. Before the October 2026 pipeline update, such an entry was published with the quoteamountas its quantity.
Column types and descriptions: Liquidations schema.