Bitget APIBitget API
UTAClassic
Legacy Doc
  • Overview
  • API Documentation
  • WebSocket
  • Agent Hub
  • SDK
  • Changelog
Copied to clipboard
Websocket
Public
Private
Reality
SBE
    SBE IntroductionSBE BBO Integration GuideSBE Level 50 Integration GuideSBE Public Trade Integration Guide
SBE

SBE Level 50 Integration Guide

Overview

FieldDescription
Topicbooks50
TemplateId1001
Schema Version5
FormatSBE binary frame (opcode = 2), little-endian
DepthProvides 50-level bid/ask order book data. The first frame after subscription is a full snapshot (depthAction = 0); subsequent frames are incremental updates (depthAction = 1) to be merged into the local order book
UnitsTimestamps in microseconds (µs), but only accurate to milliseconds. Append 000 to a millisecond timestamp to get the microsecond format. E.g., millisecond timestamp 1726233600001 corresponds to µs timestamp 1726233600001000
Update Frequency20ms

Connection

  • WebSocket URL: wss://ws.bitget.com/v3/ws/public/sbe
  • Heartbeat: Send text frame "ping" every 30 seconds, server responds with "pong"
  • Message Protocol: JSON text frames during subscription phase; SBE binary frames during market data push phase, distinguished by WebSocket opCode (opCode=1 for text, opCode=2 for binary)
  • The seq field is a monotonically increasing sequence number used to detect out-of-order packets
  • The pseq field carries the seq of the previous frame, used together with seq to detect packet loss. It is 0 for snapshot frames

Subscription Flow

1. Send Subscription Request

Code
{ "op": "subscribe", "args": [ { "instType": "usdt-futures", "topic": "books50", "symbol": "BTCUSDT" } ] }

Parameter Description:

ParameterTypeDescription
instTypestringProduct type: spot
usdt-futures
usdc-futures
coin-futures
topicstringFixed value: books50
symbolstringSymbol name, e.g. BTCUSDT, ETHUSDT

2. Subscription Confirmation

Code
{ "event": "subscribe", "arg": { "instType": "usdt-futures", "topic": "books50", "symbol": "BTCUSDT" } }

3. Receiving Data

After subscription confirmation, an SBE binary frame is pushed every 20ms. The first frame after subscription is a full snapshot (depthAction = 0); all subsequent frames are incremental updates (depthAction = 1). The depthAction field distinguishes them explicitly — merge each update frame into the local order book built from the preceding snapshot.

4. Unsubscribe

Code
{ "op": "unsubscribe", "args": [ { "instType": "usdt-futures", "topic": "books50", "symbol": "BTCUSDT" } ] }

SBE Message Structure

Price/Size Calculation Formula

Code
actual_value = mantissa × 10^exponent

Example: mantissa = 123456, exponent = -4, represents 12.3456 (actual_value = mantissa × 10 ^ exponent)

Common Message Header (8 bytes)

All SBE messages must include a fixed 8-byte header for parsing and identifying subsequent data.

FieldTypeLength (Byte)Description
blockLengthuint162Root block length
templateIduint162Channel unique identifier, fixed value = 1001
schemaIduint162Schema ID
versionuint162Schema version

Message Field Definitions

Note: priceExponent and sizeExponent are fixed values in the XML Schema. Prices and sizes are represented using mantissa and exponent.

#FieldTypeDescription
-messageHeaderCompositeFixed header, contains blockLength, templateId, schemaId, version
1tsuint64Matching engine timestamp
µs timestamp, but only accurate to milliseconds. Append 000 to a millisecond timestamp to get the microsecond format. E.g., millisecond timestamp 1726233600001 → µs 1726233600001000
2sequint64Message sequence number, used to detect packet loss or out-of-order delivery
3priceExponentint8Price exponent
4sizeExponentint8Size exponent
5stsuint64Stream service push timestamp in microseconds
6categoryuint8Product line: spot / usdt-futures / coin-futures / usdc-futures
7psequint64The seq of the previous frame, used to detect packet loss or out-of-order delivery of incremental updates. It is 0 for snapshot frames
8depthActionuint8Push action: 0 = Snapshot (full snapshot), 1 = Update (incremental update, only changed levels are included)
100paddinguint8Padding bytes
200asksgroupSize16EncodingFixed 50-entry ask array
201bidsgroupSize16EncodingFixed 50-entry bid array
300symbolvarString[8]Symbol name, UTF-8 format

Asks Group

#FieldTypeDescription
1priceint64Ask price
Actual price = price × 10^priceExponent
2sizeint64Ask size
Actual size = size × 10^sizeExponent

Bids Group

#FieldTypeDescription
1priceint64Bid price
Actual price = price × 10^priceExponent
2sizeint64Bid size
Actual size = size × 10^sizeExponent

Depth Action Values

The depthAction field tells you how to interpret the asks / bids groups of the current frame.

ValueNameDescription
0SnapshotFull snapshot. The asks / bids groups contain the complete level set, and pseq is 0
1UpdateIncremental update. Only the levels that changed since the previous frame are included; a level pushed with size = 0 means that price level was removed

For depthAction = 1 frames, apply the received levels onto the local order book: overwrite the price level with the new size, or delete it when size = 0.

Binary Layout Overview

Code
┌─────────────┬──────────────┬────────────────────────┬────────────────────────┬──────────┐ │ Header (8B) │ Root (40B) │ Asks: GrpHdr(4B)+N×16B │ Bids: GrpHdr(4B)+N×16B │ Symbol │ └─────────────┴──────────────┴────────────────────────┴────────────────────────┴──────────┘

Decoding Example

Raw Binary (hex)

The frame below is an incremental update (depthAction = 1) carrying the changed ask and bid levels only. A Snapshot frame (depthAction = 0) has the same layout, except that pseq is 0 and the asks / bids groups hold the complete level set.

Code
28 00 E9 03 01 00 05 00 <- header: blockLength=40, templateId=1001, schemaId=1, version=5 00 40 1E 18 24 0A 06 00 <- ts (uint64 LE) = 1700000000000000 2A 00 00 00 00 00 00 00 <- seq (uint64 LE) = 42 FE <- priceExponent (int8) = -2 FC <- sizeExponent (int8) = -4 E8 43 1E 18 24 0A 06 00 <- sts (uint64 LE) = 1700000000001000 01 <- category (uint8) = 1 (usdt-futures) 29 00 00 00 00 00 00 00 <- pseq (uint64 LE) = 41 01 <- depthAction (uint8) = 1 (Update) 00 00 00 00 <- padding4 10 00 02 00 <- asks group: entryBlockLength=16, numInGroup=2 52 33 64 00 00 00 00 00 <- asks[0].price = 6566738 10 27 00 00 00 00 00 00 <- asks[0].size = 10000 B6 33 64 00 00 00 00 00 <- asks[1].price = 6566838 20 4E 00 00 00 00 00 00 <- asks[1].size = 20000 10 00 02 00 <- bids group: entryBlockLength=16, numInGroup=2 26 32 64 00 00 00 00 00 <- bids[0].price = 6566438 30 75 00 00 00 00 00 00 <- bids[0].size = 30000 C2 31 64 00 00 00 00 00 <- bids[1].price = 6566338 40 9C 00 00 00 00 00 00 <- bids[1].size = 40000 07 42 54 43 55 53 44 54 <- symbol: length=7, "BTCUSDT"

Decoded JSON

Code
{ "header": { "block_length": 40, "template_id": 1001, "schema_id": 1, "version": 5 }, "ts": 1700000000000000, "seq": 42, "price_exponent": -2, "size_exponent": -4, "sts": 1700000000001000, "category": 1, "pseq": 41, "depth_action": 1, "asks": [ { "price": "65667.38", "size": "1.0000" }, { "price": "65668.38", "size": "2.0000" } ], "bids": [ { "price": "65664.38", "size": "3.0000" }, { "price": "65663.38", "size": "4.0000" } ], "symbol": "BTCUSDT" }

Python Integration Example

Code
"""Bitget books50 SBE WebSocket subscription example""" import asyncio import json import struct from decimal import Decimal import websockets WS_URL = "wss://ws.bitget.com/v3/ws/public/sbe" INST_TYPE = "usdt-futures" SYMBOL = "BTCUSDT" TOPIC = "books50" DEPTH_ACTION = {0: "Snapshot", 1: "Update"} def decode_depth50(data: bytes) -> dict: """Decode Depth50 (templateId=1001) SBE frame""" block_length, template_id, schema_id, version = struct.unpack_from('<HHHH', data, 0) assert template_id == 1001, f"unexpected templateId: {template_id}" offset = 8 base = offset ts, = struct.unpack_from('<Q', data, offset); offset += 8 seq, = struct.unpack_from('<Q', data, offset); offset += 8 price_exp, = struct.unpack_from('<b', data, offset); offset += 1 size_exp, = struct.unpack_from('<b', data, offset); offset += 1 sts, = struct.unpack_from('<Q', data, offset); offset += 8 category, = struct.unpack_from('<B', data, offset); offset += 1 # sinceVersion=5: pseq + depthAction pseq = depth_action = None if version >= 5: pseq, = struct.unpack_from('<Q', data, offset); offset += 8 depth_action, = struct.unpack_from('<B', data, offset); offset += 1 # Skip padding, based on blockLength offset = base + block_length def decode_group(off): entry_bl, num = struct.unpack_from('<HH', data, off); off += 4 levels = [] for _ in range(num): start = off p, = struct.unpack_from('<q', data, off); off += 8 s, = struct.unpack_from('<q', data, off); off += 8 off = start + entry_bl levels.append({ "price": str(Decimal(p) * Decimal(10) ** price_exp), "size": str(Decimal(s) * Decimal(10) ** size_exp), }) return levels, off asks, offset = decode_group(offset) bids, offset = decode_group(offset) sym_len, = struct.unpack_from('<B', data, offset); offset += 1 symbol = data[offset:offset + sym_len].decode('utf-8') return { "ts": ts, "seq": seq, "pseq": pseq, "price_exponent": price_exp, "size_exponent": size_exp, "sts": sts, "category": category, "depth_action": depth_action, "asks": asks, "bids": bids, "symbol": symbol, } async def main(): async with websockets.connect(WS_URL) as ws: # Subscribe await ws.send(json.dumps({ "op": "subscribe", "args": [{"instType": INST_TYPE, "topic": TOPIC, "symbol": SYMBOL}] })) print(f"[SUB] {INST_TYPE} {TOPIC} {SYMBOL}") # Heartbeat async def ping_loop(): while True: await asyncio.sleep(20) await ws.send("ping") print("[PING] sent") asyncio.create_task(ping_loop()) async for message in ws: if isinstance(message, bytes): try: msg = decode_depth50(message) ts_ms = msg['ts'] // 1000 action = DEPTH_ACTION.get(msg['depth_action'], '?') print(f"\n[Depth50] {msg['symbol']} ts={ts_ms}ms seq={msg['seq']} pseq={msg['pseq']} sts={msg['sts']} category={msg['category']}") print(f" price_exp={msg['price_exponent']} size_exp={msg['size_exponent']} action={action}") print(f" asks ({len(msg['asks'])} levels):") for i, lvl in enumerate(msg['asks'][:5]): print(f" [{i}] price={lvl['price']} size={lvl['size']}") print(f" bids ({len(msg['bids'])} levels):") for i, lvl in enumerate(msg['bids'][:5]): print(f" [{i}] price={lvl['price']} size={lvl['size']}") except Exception as e: print(f"[ERROR] {e} raw={message.hex()}") else: if message == "pong": print("[PONG] received") else: print(f"[TEXT] {message}") if __name__ == "__main__": asyncio.run(main())
SBE BBO Integration GuideSBE Public Trade Integration Guide
On this page
  • Overview
  • Connection
  • Subscription Flow
    • 1. Send Subscription Request
    • 2. Subscription Confirmation
    • 3. Receiving Data
    • 4. Unsubscribe
  • SBE Message Structure
    • Price/Size Calculation Formula
    • Common Message Header (8 bytes)
    • Message Field Definitions
    • Binary Layout Overview
  • Decoding Example
    • Raw Binary (hex)
    • Decoded JSON
  • Python Integration Example
JSON
JSON
JSON
Javascript
Javascript
Javascript
Javascript