Exchange Wire Formats

Bitget Spot wire format

How Bitget 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 Bitget 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.

The collector subscribes to the books, trade and ticker channels with instType SPOT for every online spot pair that Bitget does not restrict by region (areaSymbol no). The examples use BTCUSDT. The messages and the conversion are the same as for Bitget Futures.

  • Public WebSocket V2 (order book, trades, ticker): wss://ws.bitget.com/v2/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.

WebSocket message · tradereceived 2026-10-05 19:11:22.145390 UTC
{
"action": "snapshot",
"arg": {
"instType": "SPOT",
"channel": "trade",
"instId": "BTCUSDT"
},
"data": [
{
"ts": "1791227477947",
"price": "85640.81",
"size": "0.000167",
"side": "sell",
"tradeId": "1491081603666657280"
},
{
"ts": "1791227476436",
"price": "85640.82",
"size": "0.000292",
"side": "buy",
"tradeId": "1491081597329063936"
},
{
"ts": "1791227476429",
"price": "85638.99",
"size": "0.000292",
"side": "buy",
"tradeId": "1491081597299703808"
}
],
"ts": 1791227482019
}

Published rows · 3

received_time1791227482145390000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bitget.
event_time1791227476429data[].tsCopied from the trade's fill time data[].ts, not the envelope ts. Milliseconds.
symbolBTCUSDTarg.instIdCopied.
trade_id1491081597299703808data[].tradeIdCopied, stored as an integer.
price85638.99data[].priceCopied as the exact decimal string.
quantity0.000292data[].sizeCopied as the exact decimal string. Base asset units.
trade_time1791227476429data[].tsCopied. Milliseconds. Same value as event_time.
is_buyer_makerfalsedata[].sidetrue when the taker side side is sell, false when it is buy.
order_typeMARKET—Always 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.

WebSocket message · tradereceived 2026-10-05 19:11:35.006347 UTC
{
"action": "update",
"arg": {
"instType": "SPOT",
"channel": "trade",
"instId": "BTCUSDT"
},
"data": [
{
"ts": "1791227494878",
"price": "85639.46",
"size": "0.000583",
"side": "sell",
"tradeId": "1491081674680418304"
}
],
"ts": 1791227494879
}

Published row

received_time1791227495006347000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bitget.
event_time1791227494878data[].tsCopied from the trade's fill time data[].ts, not the envelope ts. Milliseconds.
symbolBTCUSDTarg.instIdCopied.
trade_id1491081674680418304data[].tradeIdCopied, stored as an integer.
price85639.46data[].priceCopied as the exact decimal string.
quantity0.000583data[].sizeCopied as the exact decimal string. Base asset units.
trade_time1791227494878data[].tsCopied. Milliseconds. Same value as event_time.
is_buyer_makertruedata[].sidetrue when the taker side side is sell, false when it is buy.
order_typeMARKET—Always MARKET. Bitget does not send an order type; this is a placeholder.

Notes

  • Bitget's side is the taker's side. Bitget sends no order type, so order_type is always MARKET; use is_buyer_maker for the aggressor side.
  • Trade IDs increase over time but are not consecutive. Do not use gaps in trade_id to 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.

WebSocket message · booksreceived 2026-10-05 19:11:22.328415 UTC
{
"action": "snapshot",
"arg": {
"instType": "SPOT",
"channel": "books",
"instId": "BTCUSDT"
},
"data": [
{
"asks": [
["85639.47", "0.329203"],
["85643.99", "0.000527"],
["85644", "0.000527"]
],
"bids": [
["85639.46", "0.316465"],
["85638.38", "0.00253"],
["85638.37", "0.060218"]
],
"ts": "1791227481900",
"seq": 885104208381,
"pseq": 0
}
],
"ts": 1791227481952
}

Published rows · 6

received_time1791227482328415000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bitget.
event_time1791227481952tsCopied from the envelope ts, Bitget's push time. Milliseconds.
transaction_time1791227481900data[].tsCopied from data[].ts, the matching-engine time. Milliseconds.
symbolBTCUSDTarg.instIdCopied.
event_typesnapshotactionsnapshot when action is snapshot.
first_update_idnull—Null. Bitget sends one sequence number per push, not a range.
final_update_id885104208381data[].seqCopied.
prev_final_update_idnull—Null for snapshots: a snapshot starts a new seq chain. Bitget sends pseq 0 on snapshots.
last_update_idnull—Null. Bitget sends no separate last update ID.
sidebiddata[].bidsdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85639.46data[].bids[][0]data[].asks[][0]Copied as the exact decimal string.
quantity0.316465data[].bids[][1]data[].asks[][1]Copied as the exact decimal string: the total size at that price. Base asset units.
order_countnull—Null. Bitget's 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 no bid levels and 9 ask levels in this update; 3 of the ask levels are shown.

WebSocket message · booksreceived 2026-10-05 19:11:22.328616 UTC
{
"action": "update",
"arg": {
"instType": "SPOT",
"channel": "books",
"instId": "BTCUSDT"
},
"data": [
{
"asks": [
["85645.91", "0"],
["85666.66", "0.233691"],
["85679.99", "0.233541"]
],
"bids": [],
"ts": "1791227482000",
"seq": 885104210890,
"pseq": 885104208381
}
],
"ts": 1791227482001
}

Published rows · 3

received_time1791227482328616000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bitget.
event_time1791227482001tsCopied from the envelope ts, Bitget's push time. Milliseconds.
transaction_time1791227482000data[].tsCopied from data[].ts, the matching-engine time. Milliseconds.
symbolBTCUSDTarg.instIdCopied.
event_typeupdateactionupdate when action is update.
first_update_idnull—Null. Bitget sends one sequence number per push, not a range.
final_update_id885104210890data[].seqCopied.
prev_final_update_id885104208381data[].pseqCopied. The 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.
last_update_idnull—Null. Bitget sends no separate last update ID.
sideaskdata[].bidsdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85645.91data[].asks[][0]Copied as the exact decimal string.
quantity0data[].asks[][1]Copied as the exact decimal string: the new total size at that price. Base asset units. 0 removes the level.
order_countnull—Null. Bitget's books channel does not report order counts.

Notes

  • transaction_time holds the matching-engine time from the October 2026 pipeline update; it is null in files written before it. event_time has always been the push time.
  • Each update's prev_final_update_id (pseq) equals the previous message's final_update_id (seq). A mismatch means a lost message; resume from the next snapshot. Bitget's spot documentation does not list pseq, but the spot books channel sends it; rows from messages without it have a null prev_final_update_id.
  • A message with no levels at all is published as one row with side noop, price 0 and quantity 0, so the seq chain stays complete. A noop update changes no level. A noop snapshot 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_time is the hour start, transaction_time is null, and final_update_id and last_update_id both hold the seq of the last message applied. The next update's prev_final_update_id links to it.

Column types and descriptions: Order Book schema.

Ticker

One row per ticker push.

  • WebSocket ticker
WebSocket message · tickerreceived 2026-10-05 19:11:22.218598 UTC
{
"action": "snapshot",
"arg": {
"instType": "SPOT",
"channel": "ticker",
"instId": "BTCUSDT"
},
"data": [
{
"instId": "BTCUSDT",
"lastPr": "85640.81",
"open24h": "85265.09",
"high24h": "86993.9",
"low24h": "84980.62",
"change24h": "0.00301",
"bidPr": "85639.46",
"askPr": "85639.47",
"bidSz": "0.310626",
"askSz": "0.507108",
"baseVolume": "2723.070875",
"quoteVolume": "234243302.169137",
"openUtc": "86511.00",
"changeUtc24h": "-0.01006",
"ts": "1791227482089"
}
],
"ts": 1791227482092
}

Published row

received_time1791227482218598000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bitget.
event_time1791227482092tsCopied from the envelope ts, Bitget's push time. Milliseconds.
symbolBTCUSDTdata[].instIdCopied.
price_changenull—Null. Bitget does not send the absolute 24h price change.
price_change_percent0.301data[].change24hchange24h multiplied by 100. Bitget sends a fraction (0.01 is 1 %); the column is in percent.
weighted_average_pricenull—Null. Bitget does not send a weighted average price.
last_price85640.81data[].lastPrCopied.
last_quantitynull—Null. Bitget's ticker does not send the last trade size.
open_price85265.09data[].open24hCopied. The price 24 hours ago (rolling window), not the UTC-day open.
high_price86993.9data[].high24hCopied. Rolling 24 hours.
low_price84980.62data[].low24hCopied. Rolling 24 hours.
base_asset_volume2723.070875data[].baseVolumeCopied. 24h volume in base asset units.
quote_asset_volume234243302.169137data[].quoteVolumeCopied. 24h volume in quote asset units.
statistics_open_timenull—Null. Bitget does not send the window bounds.
statistics_close_timenull—Null. Bitget does not send the window bounds.
first_trade_idnull—Null. Bitget does not send trade ID ranges.
last_trade_idnull—Null. Bitget does not send trade ID ranges.
total_tradesnull—Null. Bitget does not send a trade count.

Notes

  • price_change_percent is in percent from the October 2026 pipeline update. Files written before it hold Bitget's fraction unchanged (0.01 for 1 %); multiply those values by 100.
  • The ticker's best bid and ask (bidPr, askPr, bidSz, askSz), openUtc and changeUtc24h are not published.

Column types and descriptions: Ticker schema.

Not published

  • Mark Price: Spot markets have no mark price; the empty mark_price files published from 2025-07-15 to 2026-09-20 are scheduled for removal.
  • Open Interest: Spot markets have no open interest; the empty open_interest files published from 2025-07-15 to 2026-09-20 are scheduled for removal.
  • Liquidations: Bitget publishes no spot liquidation feed; its liquidation topic covers only the futures product types.