Commands
Commands & Subscriptions
This page covers the request/response commands available on the WebSocket API: querying instruments, managing subscriptions, and recovering missed messages.
Server Message Envelope
Every message sent by the server (both events and responses) includes the following fields:
| Field | Type | Description |
|---|---|---|
kind | string | Message category: "event" or "response" |
type | string | Specific message type (e.g., "trade", "auth", "index_price") |
timestamp_ms | number | Server timestamp in milliseconds (Unix epoch) |
message_id | string | Unique message identifier (UUID v4) for deduplication. If you receive the same message_id twice, the second is a duplicate and can be safely discarded. |
seq_id | number | Monotonically increasing sequence number per connection, starting at 1. Used for message ordering and gap detection. If you observe a gap in seq_id values (e.g., received 5 then 7, missing 6), messages were lost and can be recovered using resend. |
Example:
{
"kind": "event",
"type": "trade",
"timestamp_ms": 1677721600000,
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"seq_id": 42,
"data": { ... },
"subscription": { ... }
}Request ID Correlation
All requests support an optional id field. The server echoes it back in the response for request-response matching:
// Request
{ "type": "auth", "id": "auth-request-1", "api_key": "your-api-key-here" }
// Response
{
"kind": "response",
"type": "auth",
"id": "auth-request-1",
"timestamp_ms": 1677721600000,
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"seq_id": 1,
"success": true
}get_instruments
Retrieve available instruments for specified markets.
Request:
{
"type": "get_instruments",
"markets": ["BTC", "ETH", "ARB"]
}The
marketsarray should contain base tokens only (not pairs).
Response:
{
"kind": "response",
"type": "get_instruments",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"instruments": [
"BTC_USDC-31OCT25-130000-C",
"BTC_USDC-PERPETUAL",
"ETH_USDC-31OCT25-4700-P"
]
}get_ob_state_by_instruments
Get the current orderbook state for specified instruments.
Request:
{
"type": "get_ob_state_by_instruments",
"instrument_names": ["BTC_USDC-31OCT25-130000-C", "BTC_USDC-PERPETUAL"]
}The
instrument_namesparameter is optional. If not provided, returns orderbook state for all instruments.
get_ob_state_by_market
Get the current orderbook state for all instruments in a specific market.
Request:
{
"type": "get_ob_state_by_market",
"market": "BTC",
"maturity": "31OCT25"
}The
maturityparameter is optional. If provided, filters instruments by expiration date.
Orderbook State Response Format
Both get_ob_state_by_instruments and get_ob_state_by_market return responses with type get_ob_state. The response structure varies by instrument type:
Options:
{
"kind": "response",
"type": "get_ob_state",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"state": {
"BTC_USDC-31OCT25-130000-C": {
"timestamp": 1677721600000,
"instrument_name": "BTC_USDC-31OCT25-130000-C",
"index_price": 45000.5,
"open_interest": 150.5,
"last_price": 2500.0,
"best_bid_price": 2495.0,
"best_bid_amount": 1.5,
"best_ask_price": 2505.0,
"best_ask_amount": 1.0,
"bids": [
[2495.0, 1.5, "order_id_abc123"],
[2490.0, 2.0, "order_id_def456"]
],
"asks": [
[2505.0, 1.0, "order_id_ghi789"],
[2510.0, 1.8, "order_id_jkl012"]
],
"positions": 25,
"mark_iv": 0.65,
"bid_iv": 0.64,
"ask_iv": 0.66,
"interest_rate": 0.05,
"settlement_price": 0,
"greeks": {
"delta": 0.45,
"gamma": 0.0012,
"theta": -45.5,
"vega": 125.3,
"rho": 85.2
}
}
}
}Perpetuals:
{
"kind": "response",
"type": "get_ob_state",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"state": {
"BTC_USDC-PERPETUAL": {
"timestamp": 1677721600000,
"instrument_name": "BTC_USDC-PERPETUAL",
"index_price": 45000.5,
"open_interest": 2500000,
"last_price": 45010.0,
"current_funding": 0.0001,
"best_bid_price": 45000.0,
"best_bid_amount": 20000,
"best_ask_price": 45020.0,
"best_ask_amount": 30000,
"bids": [
[45000.0, 20000, "order_id_xyz789"],
[44980.0, 40000, "order_id_uvw456"]
],
"asks": [
[45020.0, 30000, "order_id_rst123"],
[45040.0, 20000, "order_id_mno789"]
]
}
}
}Orderbook Array Format:
The bids and asks arrays contain price level information: [price, amount, order_id]
| Index | Field | Description |
|---|---|---|
| 0 | price | Price level (number) |
| 1 | amount | Total volume at this price level (number) |
| 2 | order_id | Unique identifier for the order (string) |
subscribe
Subscribe to specific data channels.
Request:
{
"type": "subscribe",
"subscriptions": [
{
"channel": "index_price",
"query": {
"pair": "BTC_USDC"
}
},
{
"channel": "orderbook_options",
"query": {
"instrument_name": "BTC_USDC-31OCT25-130000-C"
}
}
]
}Response:
{
"kind": "response",
"type": "subscribe",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"subscriptions": [
{
"channel": "index_price",
"query": { "pair": "BTC_USDC" }
},
{
"channel": "orderbook_options",
"query": { "instrument_name": "BTC_USDC-31OCT25-130000-C" }
}
]
}unsubscribe
Unsubscribe from specific data channels.
Request:
{
"type": "unsubscribe",
"subscriptions": [
{
"channel": "index_price",
"query": { "pair": "BTC_USDC" }
}
]
}Response:
{
"kind": "response",
"type": "unsubscribe",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"subscriptions": [
{
"channel": "index_price",
"query": { "pair": "BTC_USDC" }
}
]
}unsubscribe_all
Unsubscribe from all data channels.
Request:
{
"type": "unsubscribe_all"
}Response:
{
"kind": "response",
"type": "unsubscribe_all",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true
}get_subscriptions
List your active subscriptions.
Request:
{
"type": "get_subscriptions"
}Response:
{
"kind": "response",
"type": "get_subscriptions",
"id": "optional-request-id",
"timestamp_ms": 1677721600000,
"success": true,
"subscriptions": [
{
"channel": "index_price",
"query": { "pair": "BTC_USDC" }
},
{
"channel": "orderbook_options",
"query": { "instrument_name": "BTC_USDC-31OCT25-130000-C" }
}
]
}resend (Message Recovery)
Request the server to re-send previously delivered messages by their seq_id range. Use this when you detect a gap in seq_id values, indicating missed messages.
Request:
{
"type": "resend",
"id": "resend-request-1",
"begin_seq_id": 10,
"end_seq_id": 15
}Parameters:
| Parameter | Required | Description |
|---|---|---|
begin_seq_id | Yes | First sequence ID to resend (inclusive) |
end_seq_id | Yes | Last sequence ID to resend (inclusive) |
begin_seq_idmust be less than or equal toend_seq_id- Maximum range: 100 messages per request
end_seq_idmust not exceed the current connection's sequence ID
Success Response:
The server first re-delivers the cached messages (each with their original seq_id and message_id), then sends the resend confirmation:
{
"kind": "response",
"type": "resend",
"id": "resend-request-1",
"timestamp_ms": 1677721600000,
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"success": true,
"begin_seq_id": 10,
"end_seq_id": 15,
"messages_sent": 6
}Error - Range too large:
{
"kind": "response",
"type": "error",
"id": "resend-request-1",
"timestamp_ms": 1677721600000,
"success": false,
"error": {
"type": "PAYLOAD_VALIDATION_ERROR",
"message": "Resend range too large: 150 messages requested, maximum is 100"
}
}Error - Messages expired from cache:
{
"kind": "response",
"type": "error",
"id": "resend-request-1",
"timestamp_ms": 1677721600000,
"success": false,
"error": {
"type": "PAYLOAD_VALIDATION_ERROR",
"message": "Some messages in the requested range are no longer available in the cache (expired or evicted)",
"data": {
"missing_seq_ids": [10, 11]
}
}
}Messages are cached per connection with a limited capacity (LRU eviction) and time-to-live. Old messages may no longer be available. Resent messages retain their original
seq_idandmessage_idvalues — usemessage_idfor deduplication. Theresendrequest requires authentication. Rate limited to 5 requests per 10 seconds per API key owner.
Gap Detection Example
let expectedSeqId = 1;
function handleMessage(message: any) {
if (message.seq_id && message.seq_id > expectedSeqId) {
// Gap detected - request missing messages
ws.send(JSON.stringify({
type: 'resend',
begin_seq_id: expectedSeqId,
end_seq_id: message.seq_id - 1
}));
}
// Only advance for new in-sequence messages;
// resent messages have lower seq_id and should not rewind the counter
if (message.seq_id && message.seq_id >= expectedSeqId) {
expectedSeqId = message.seq_id + 1;
}
}Subscription Filtering
Many channels support advanced filtering to reduce the volume of events you receive.
Orderbook Filtering
For orderbook_options and orderbook_perps channels, you can filter events by:
pair(optional): Subscribe to all instruments for a specific trading pairdirection: Filter for only "buy" or "sell" ordersoptions.skip_snapshot: Skip initial orderbook state, receive only incremental updates
Maker filtering has been moved to the dedicated
orderbook_makerchannel.
Example - Subscribe to all BTC options with buy orders:
{
"channel": "orderbook_options",
"query": {
"pair": "BTC_USDC",
"direction": "buy"
}
}Example - Subscribe to all perpetual markets without initial snapshots:
{
"channel": "orderbook_perps",
"query": {
"options": {
"skip_snapshot": true
}
}
}Query Options
| Option | Type | Default | Description |
|---|---|---|---|
skip_snapshot | boolean | false | Skip the initial orderbook snapshot when subscribing. Only applies to orderbook_options and orderbook_perps channels. When set to true, you'll only receive incremental updates (post_order, cancel_order, update_order events) without the initial state. Note: Skipping the snapshot and relying only on duplicate subscription detection prevents sending snapshots for already-subscribed channels. |
Updated 1 day ago
