Exchange Wire Formats

Bybit Spot wire format

How Bybit 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 Bybit 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 orderbook.50.<symbol>, publicTrade.<symbol> and tickers.<symbol> for every trading spot pair. Messages are parsed and postprocessed by the same code as Bybit Futures.

  • Public WebSocket: wss://stream.bybit.com/v5/public/spot
  • REST (trade backfill): https://api.bybit.com

Exchange API reference: bybit-exchange.github.io. Conventions shared by every exchange are on the wire formats overview.

Trades

One row per trade from the publicTrade.<symbol> topic, plus trades recovered from REST after a reconnect.

  • WebSocket publicTrade.<symbol>
  • REST GET /v5/market/recent-trade

Trade message

Bybit batches trades: this message carries 2, and each becomes its own row.

WebSocket message · publicTrade.<symbol>received 2026-10-05 19:15:00.442966 UTC
{
"topic": "publicTrade.BTCUSDT",
"ts": 1791227700350,
"type": "snapshot",
"data": [
{
"i": "2290000001224566789",
"T": 1791227700349,
"p": "85663.7",
"v": "0.0024",
"S": "Sell",
"seq": 114993707179,
"s": "BTCUSDT",
"BT": false,
"RPI": false
},
{
"i": "2290000001224566790",
"T": 1791227700349,
"p": "85663.7",
"v": "0.002853",
"S": "Sell",
"seq": 114993707179,
"s": "BTCUSDT",
"BT": false,
"RPI": false
}
]
}

Published rows · 2

received_time1791227700442966000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bybit.
event_time1791227700350tsCopied from the message ts, when Bybit generated the push. Milliseconds. Shared by every trade in the message.
symbolBTCUSDTdata[].sCopied.
trade_id2290000001224566789data[].iBybit sends a numeric string. Published as the same integer.
price85663.7data[].pCopied as the exact decimal string Bybit sent.
quantity0.0024data[].vCopied as the exact decimal string. Base asset units.
trade_time1791227700349data[].TCopied. When the trade was filled, milliseconds.
is_buyer_makertruedata[].Strue when the taker side S is Sell, false when it is Buy.
order_typeMarket—Always Market. A placeholder: Bybit sends no order type.

REST trade backfill

Bybit does not replay trades on subscribe. After every trade connection (re)opens, the collector fetches the latest trades from GET /v5/market/recent-trade and publishes those it did not receive over the WebSocket. A backfilled trade whose trade_time falls outside the hour of the file is not published.

Abridged: Requested with limit=1 to keep the example short; the collector requests limit=60, the most Bybit returns.

REST responsereceived 2026-10-05 19:18:36.073946 UTC
GET https://api.bybit.com/v5/market/recent-trade?category=spot&symbol=BTCUSDT&limit=1
{
"retCode": 0,
"retMsg": "OK",
"result": {
"category": "spot",
"list": [
{
"execId": "2290000001224568695",
"symbol": "BTCUSDT",
"price": "85782.8",
"size": "0.005246",
"side": "Sell",
"time": "1791227915828",
"isBlockTrade": false,
"isRPITrade": false,
"seq": "114993797995"
}
]
},
"retExtInfo": {},
"time": 1791227915979
}

Published row

received_time1791227916073946027—Our collector's clock when the REST response arrived, in nanoseconds since the Unix epoch. Later than the trade, often by seconds.
event_time1791227915828result.list[].timeThe trade time: REST responses have no push time, so event_time equals trade_time on backfilled rows. Milliseconds.
symbolBTCUSDTresult.list[].symbolCopied.
trade_id2290000001224568695result.list[].execIdBybit sends a numeric string. Published as the same integer.
price85782.8result.list[].priceCopied as the exact decimal string.
quantity0.005246result.list[].sizeCopied as the exact decimal string. Base asset units.
trade_time1791227915828result.list[].timeParsed from the string. Milliseconds.
is_buyer_makertrueresult.list[].sidetrue when the taker side side is Sell, false when it is Buy.
order_typeMarket—Always Market. A placeholder: Bybit sends no order type.

Notes

  • Rows are sorted by trade_time, then Bybit's cross sequence seq, then trade_id.
  • The block-trade flag BT, the RPI flag RPI, the tick direction L and seq are not published. Block trades and RPI trades are published as ordinary rows.
  • A trade received over both the WebSocket and the REST backfill is published once, with the WebSocket values. On a backfilled row event_time equals trade_time, and received_time can be seconds after the trade.
  • Bybit's REST endpoint returns only the latest 60 spot trades, so a longer reconnect gap is only partly recovered.

Column types and descriptions: Trades schema.

Order Book

One row per price level from the orderbook.50.<symbol> topic, published as Bybit sent it.

  • WebSocket orderbook.50.<symbol>

Book snapshot

Bybit sends a snapshot of the 50-level book when the subscription starts, and again after a problem on its side or a service restart (u = 1). Each level becomes a snapshot row.

Abridged: Bybit sent 50 bid and 50 ask levels; the best three of each are shown.

WebSocket message · orderbook.50.<symbol>received 2026-10-05 19:14:58.426402 UTC
{
"topic": "orderbook.50.BTCUSDT",
"ts": 1791227698276,
"type": "snapshot",
"data": {
"s": "BTCUSDT",
"b": [
["85666.4", "0.134992"],
["85666.3", "0.000915"],
["85665.9", "0.006238"]
],
"a": [
["85666.5", "0.596838"],
["85666.7", "0.024785"],
["85667.8", "0.000835"]
],
"u": 311718506,
"seq": 114993705748
},
"cts": 1791227698274
}

Published rows · 6

received_time1791227698426402000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bybit.
event_time1791227698276tsCopied. When Bybit generated the push, milliseconds.
transaction_time1791227698274ctsCopied. Matching engine time, milliseconds.
symbolBTCUSDTtopicThe symbol part of the topic (orderbook.50.BTCUSDT). Equals data.s.
event_typesnapshottypesnapshot for type: "snapshot": every level of the 50-level book. A message with u = 1 (Bybit service restart) is also a snapshot.
first_update_idnull—Null for snapshots.
final_update_id311718506data.uCopied. Starts a new chain: the next delta has u + 1.
prev_final_update_idnull—Null. Bybit sends no previous update ID.
last_update_id114993705748data.seqCopied: Bybit's cross sequence seq. It orders messages but is not contiguous.
sidebiddata.bdata.abid for levels in b, ask for levels in a. One row per level.
price85666.4data.b[][0]data.a[][0]Copied as the exact decimal string.
quantity0.134992data.b[][1]data.a[][1]Copied as the exact decimal string.
order_countnull—Null. Bybit does not report order counts.

Book delta

WebSocket message · orderbook.50.<symbol>received 2026-10-05 19:14:58.426900 UTC
{
"topic": "orderbook.50.BTCUSDT",
"ts": 1791227698296,
"type": "delta",
"data": {
"s": "BTCUSDT",
"b": [
["85666.4", "0.139984"],
["85662.9", "0"],
["85662.2", "0.00447"]
],
"a": [
["85668.3", "0.000111"],
["85693.8", "0.075584"],
["85694.5", "0"]
],
"u": 311718507,
"seq": 114993705774
},
"cts": 1791227698295
}

Published rows · 6

received_time1791227698426900000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bybit.
event_time1791227698296tsCopied. When Bybit generated the push, milliseconds.
transaction_time1791227698295ctsCopied. Matching engine time, milliseconds.
symbolBTCUSDTtopicThe symbol part of the topic (orderbook.50.BTCUSDT). Equals data.s.
event_typeupdatetypeupdate for type: "delta".
first_update_id311718507data.uCopied from u. Each delta covers exactly one update ID, so first_update_id = final_update_id.
final_update_id311718507data.uCopied. One more than the previous message's u; a larger step is a gap.
prev_final_update_idnull—Null. Bybit sends no previous update ID.
last_update_id114993705774data.seqCopied: Bybit's cross sequence seq. It orders messages but is not contiguous.
sidebiddata.bdata.abid for levels in b, ask for levels in a. One row per level.
price85666.4data.b[][0]data.a[][0]Copied as the exact decimal string.
quantity0.139984data.b[][1]data.a[][1]Copied as the exact decimal string: the new total at that price. 0 removes the level.
order_countnull—Null. Bybit does not report order counts.

Notes

  • The collector subscribes to the 50-level book only, and WebSocket snapshots anchor it. Bybit's REST depth snapshots carry the update IDs of its 1,000-level stream, so they cannot anchor this book and are not published.
  • The collector checks that each delta's u is one more than the previous message's before it writes. After a gap it reconnects and starts again from a new WebSocket snapshot.
  • A delta with no levels is published as one row with side = noop and price and quantity 0, so the update-ID chain has no holes. Skip these rows when rebuilding the 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, final_update_id is the last u applied and last_update_id its seq.
  • Quantities are in base asset units (BTC for BTCUSDT). Bybit does not include Retail Price Improvement (RPI) orders in the book stream.
  • Files written before the October 2026 pipeline update can also contain snapshot rows built from REST depth snapshots. Their update IDs belong to Bybit's 1,000-level stream and do not continue the 50-level chain; skip them when replaying.

Column types and descriptions: Order Book schema.

Ticker

One row per tickers.<symbol> message.

  • WebSocket tickers.<symbol>

Bybit spot tickers are always full snapshots, so each row comes from one message.

WebSocket message · tickers.<symbol>received 2026-10-05 19:14:56.423462 UTC
{
"topic": "tickers.BTCUSDT",
"ts": 1791227696127,
"type": "snapshot",
"cs": 114993705064,
"data": {
"symbol": "BTCUSDT",
"lastPrice": "85666.5",
"highPrice24h": "86996.9",
"lowPrice24h": "84978.5",
"prevPrice24h": "85386",
"volume24h": "7322.439121",
"turnover24h": "630464015.32595089",
"price24hPcnt": "0.0033",
"usdIndexPrice": "85662.421591"
}
}

Published row

received_time1791227696423462000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Bybit.
event_time1791227696127tsCopied. When Bybit generated the push, milliseconds.
symbolBTCUSDTdata.symbolCopied.
price_change280.5data.lastPricedata.prevPrice24hlastPrice − prevPrice24h, computed exactly in decimal.
price_change_percent0.33data.price24hPcntBybit sends a fraction; multiplied by 100 exactly, so the column is in percent: 0.0033 becomes 0.33.
weighted_average_pricenull—Null. Bybit sends no weighted average price.
last_price85666.5data.lastPriceCopied.
last_quantitynull—Null. Bybit sends no last trade quantity.
open_price85386data.prevPrice24hCopied from prevPrice24h, the price 24 hours ago.
high_price86996.9data.highPrice24hCopied.
low_price84978.5data.lowPrice24hCopied.
base_asset_volume7322.439121data.volume24hCopied. 24h volume in base asset units.
quote_asset_volume630464015.32595089data.turnover24hCopied. 24h turnover in quote asset units.
statistics_open_timenull—Null. Bybit sends no statistics window.
statistics_close_timenull—Null. Bybit sends no statistics window.
first_trade_idnull—Null. Bybit sends no trade IDs in tickers.
last_trade_idnull—Null. Bybit sends no trade IDs in tickers.
total_tradesnull—Null. Bybit sends no trade count.

Notes

  • usdIndexPrice is not published.
  • price_change_percent is in percent from the 2026-09 data-integrity release; earlier files hold Bybit's fraction.

Column types and descriptions: Ticker schema.

Not published

  • Mark Price: Bybit spot tickers carry no mark price or funding rate.
  • Open Interest: Spot markets have no open interest.
  • Liquidations: Bybit's allLiquidation topic covers derivatives only.