Real-time orderbook updates for perpetual markets.
Channel: orderbook
Authentication: Not required
Subscribe
{
"method": "subscribe",
"params": {
"channel": "orderbook",
"market_ids": [1, 2]
}
}| Parameter | Type | Required | Description |
|---|---|---|---|
market_ids | number[] | No | Filter by market IDs. Empty = all markets |
The snapshot can arrive before the subscribe acknowledgementOne snapshot is sent per market, and it is written to the socket before the
"type": "subscribed"reply. A client that ignores messages until it sees the ack will drop its own snapshot and start with an incomplete book.
Message Format
Snapshot
Sent immediately after subscribing — one message per market, containing the full book.
{
"method": "snapshot",
"channel": "orderbook",
"type": "snapshot",
"market_id": "1",
"data": {
"market_id": 1,
"bids": [
{ "price": "63218.5", "quantity": "1.317053", "order_count": 1 },
{ "price": "63215.2", "quantity": "0.004744", "order_count": 1 }
],
"asks": [
{ "price": "63220", "quantity": "1.317053", "order_count": 1 },
{ "price": "63222.6", "quantity": "0.004744", "order_count": 1 }
]
},
"level_count": 4,
"block_number": 19328296,
"log_index": 0,
"worker_timestamp": "1786934053513837410"
}Update
Sent when the book changes. Contains only the modified price levels.
{
"channel": "orderbook",
"type": "update",
"market_id": "1",
"data": {
"market_id": 1,
"bids": [
{ "price": "62913.8", "quantity": "6.006075", "order_count": 1 }
],
"asks": [
{ "price": "63257.5", "quantity": "0.750763", "order_count": 1 },
{ "price": "63257.6", "quantity": "0", "order_count": 0 }
]
},
"checksum": 3608920187,
"block_number": 19328296,
"log_index": 614,
"tx_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"worker_timestamp": "1786934053513837410"
}Level Deletion
A removed price level is sent with quantity: "0" and order_count: 0 — as in the 63257.6 ask above. Delete that level from your local book rather than storing a zero.
Data Fields
Snapshot Message Fields
| Field | Type | Description |
|---|---|---|
method | string | Always "snapshot" |
channel | string | Always "orderbook" |
type | string | Always "snapshot" |
market_id | string | Market ID |
data | object | Contains market_id, bids, and asks arrays |
level_count | number | Total number of price levels in this snapshot |
block_number | number | Block the book state is taken at |
log_index | number | Log index within that block |
worker_timestamp | string | Server time in nanoseconds |
Update Message Fields
| Field | Type | Description |
|---|---|---|
channel | string | Always "orderbook" |
type | string | Always "update" |
market_id | string | Market ID |
data | object | Contains market_id, bids, and asks arrays |
checksum | number | CRC32 checksum of the full book after this update |
block_number | number | Blockchain block number |
log_index | number | Log index within the block |
tx_hash | string | Transaction hash |
worker_timestamp | string | Server time in nanoseconds |
There is no timestamp or block_timestamp field on this channel.
Level Object
Identical in snapshots and updates:
| Field | Type | Description |
|---|---|---|
price | string | Price level, decimal (e.g. "63250.4") |
quantity | string | Total quantity at this price, decimal. "0" indicates deletion |
order_count | number | Number of orders resting at this price level |
Levels do not carry their own block_number / log_index; the block metadata on the message applies to every level in it.
Ordering
- Bids: Sorted descending by price (highest first)
- Asks: Sorted ascending by price (lowest first)
CRC32 Checksum
The checksum lets you verify that your local book still matches the server's. It is computed over the entire book after the update, not over the levels in the message.
The checksum is computed on wei-scaled integersThe wire format is decimal, but the checksum input is not: every price and quantity is multiplied by 1018 and rendered as an integer with no decimal point before hashing. Feeding the decimal strings straight in never matches.
Algorithm
- Sort bids descending by price, asks ascending by price
- Interleave:
bids[0], asks[0], bids[1], asks[1], …— if one side is shorter, its slots are simply skipped - For each level emit
pricethenquantity, each converted to wei (18 decimals, integer) - Join every value with
:and compute CRC32-IEEE over the ASCII bytes
Python Implementation
import binascii
from decimal import Decimal
WEI = Decimal(10) ** 18
def to_wei(value: str) -> str:
return str(int(Decimal(value) * WEI))
def compute_checksum(bids, asks):
"""bids/asks: full local book, already sorted, with decimal 'price'/'quantity' strings."""
parts = []
for i in range(max(len(bids), len(asks))):
if i < len(bids):
parts.append(to_wei(bids[i]["price"]))
parts.append(to_wei(bids[i]["quantity"]))
if i < len(asks):
parts.append(to_wei(asks[i]["price"]))
parts.append(to_wei(asks[i]["quantity"]))
return binascii.crc32(":".join(parts).encode()) & 0xffffffffChecksum Mismatch
If your computed checksum doesn't match the received one:
- Your local book has drifted — do not keep trading on it
- Unsubscribe and resubscribe to get a fresh snapshot
- Check that you delete
quantity: "0"levels instead of keeping them
Examples
Subscribe to all markets
{
"method": "subscribe",
"params": {
"channel": "orderbook"
}
}Subscribe to specific markets
{
"method": "subscribe",
"params": {
"channel": "orderbook",
"market_ids": [1, 2]
}
}Unsubscribe
{
"method": "unsubscribe",
"params": {
"channel": "orderbook"
}
}Testing with wscat
# Connect (mainnet)
wscat -c wss://ws.rise.trade/ws
# Subscribe to market 1 (BTC/USDC)
{"method":"subscribe","params":{"channel":"orderbook","market_ids":[1]}}
# Unsubscribe
{"method":"unsubscribe","params":{"channel":"orderbook"}}Notes
Price Format
Prices and quantities are decimal strings, not wei. Only the oracle channel quotes in wei.
Re-subscribing
Re-subscribing to orderbook merges the new market IDs into your existing subscription and sends a snapshot only for the markets you did not already have. Re-subscribing to a market you already follow returns no new snapshot.
Public Channel
This channel does not require authentication.