Bullet Trading API WebSocket Specification
WebSocket endpoint: ws://<host>:<port>/ws
- Localnet:
ws://localhost:3000/ws - Staging:
wss://tradingapi.staging.bullet.xyz/ws - Testnet:
wss://tradingapi.testnet.bullet.xyz/ws - Mainnet:
wss://tradingapi.bullet.xyz/ws
Timestamps: all timestamps in this API are microseconds since Unix epoch (16 digits, e.g.
1706745600000000). Binance uses milliseconds (13 digits). Divide by 1,000 if converting from Bullet to Binance format.
Connection Lifecycle
WebSocket connections go through three phases: establishment, keepalive, and disconnection. The server uses status messages to inform clients of connection state changes (e.g., successful connection, impending disconnection with reason).
Status Message
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "status" | - (no equivalent) |
| E | u64 | event time (μs) | - |
| status | string | "connected" or "disconnecting" | - |
| clientId | string | UUID client identifier | - |
| reason | string? | disconnect reason (only for disconnecting) | - |
Connection Establishment
- Client connects to WebSocket endpoint
- Server assigns unique
clientId - Server sends
"connected"status message
{
"e": "status",
"E": 1706745600000000,
"status": "connected",
"clientId": "ws_abc123"
}
Disconnection
The server sends a "disconnecting" status message before closing:
{
"e": "status",
"E": 1706745600000000,
"status": "disconnecting",
"clientId": "ws_abc123",
"reason": "pong_timeout"
}
Disconnect Reasons
| Reason | Default | Description |
|---|---|---|
idle_timeout | 60s | no valid message within timeout |
pong_timeout | 60s | client didn’t respond to ping |
max_duration | 24h | connection exceeded max lifetime |
Keepalive (Ping/Pong)
Connections are kept alive via WebSocket ping/pong frames. The server sends ping frames every 30 seconds; clients must
respond with pong frames. Most WebSocket libraries answer ping frames automatically. Anything the client sends that
proves liveness resets the keepalive timeout: a pong frame, the client’s own ping frame, or an application-level
ping message.
Ping
Ping the server to check connection health. Alternatively clients can also send WebSocket ping frames.
{
"method": "ping",
"id": 4
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "ping" or "PING" | (WebSocket ping frame) |
| id | u64 | no | request correlation id | - |
Possible errors: ValidationError (message parse)
Responses: Pong
Pong
{
"e": "pong",
"id": 4,
"E": 1706745600000000
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "pong" | (WebSocket pong frame) |
| id | u64? | echoed request id | - |
| E | u64 | event time (μs) | - |
Subscription Streams
All client messages use JSON format with method field. Method names are case-insensitive.
Subscribe
Subscribe to market data topics or user data streams.
{
"method": "subscribe",
"id": 1,
"params": [
"BTC-USD@aggTrade",
"ETH-USD@depth10"
]
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "subscribe" or "SUBSCRIBE" | method: "SUBSCRIBE" |
| params | string[] | yes | array of topic strings | params |
| id | u64 | no | request correlation id | id |
Behavior: Idempotent with atomic validation. All topics validated first - if any fails, entire request fails. Already-subscribed topics silently skipped.
Possible errors: ValidationError, TooManyRequests, InvalidSubscriptionFormat, Unauthorized, InvalidSymbol,
ClientNotFound
Responses:
Subscribe (success)
{
"e": "subscribe",
"id": 1,
"E": 1706745600000000,
"result": "success"
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "subscribe" | - (Binance uses result: null) |
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| result | string | "success" | result: null |
Unsubscribe
Unsubscribe from market data topics or user data streams.
{
"method": "unsubscribe",
"id": 5,
"params": [
"BTC-USD@aggTrade"
]
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "unsubscribe" or "UNSUBSCRIBE" | method: "UNSUBSCRIBE" |
| params | string[] | yes | topics to unsubscribe | params |
| id | u64 | no | request correlation id | id |
Behavior: Idempotent - always succeeds. Invalid or non-subscribed topics silently skipped.
Possible errors: ValidationError (message parse)
Responses:
- Success: Unsubscribe
Unsubscribe (success)
{
"e": "unsubscribe",
"id": 5,
"E": 1706745600000000,
"result": "success"
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "unsubscribe" | - (Binance uses result: null) |
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| result | string | "success" | result: null |
ListSubscriptions
List all active subscriptions for the client.
{
"method": "list_subscriptions",
"id": 6
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "list_subscriptions" or "LIST_SUBSCRIPTIONS" | method: "LIST_SUBSCRIPTIONS" |
| id | u64 | no | request correlation id | id |
Possible errors: ValidationError (message parse)
Responses:
- Success: ListSubscriptions
ListSubscriptions (success)
{
"e": "list_subscriptions",
"id": 6,
"E": 1706745600000000,
"result": [
"BTC-USD@depth10",
"ETH-USD@aggTrade"
]
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "list_subscriptions" | - |
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| result | string[] | subscribed topics | result: […] |
Request-Response
OrderPlace
Place an order via WebSocket.
{
"method": "order.place",
"id": 10,
"params": {
"tx": "c2lnbmVkX3RyYW5zYWN0aW9uX2J5dGVz"
}
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "order.place" or "ORDER.PLACE" | method: "order.place" |
| params.tx | string | yes | signed base64-encoded borsh transaction bytes | (different: REST params) |
| id | u64 | no | request correlation id | id |
Building the
txpayload: see Place Orders (or Replace Orders for atomic quote refresh) for construction, Order Types and Fields forNewOrderArgsreference, and Signing for the sign → encode → submit flow.
Possible errors: ValidationError (empty tx), ServiceUnavailable (submission failed), Timeout,
NewOrderRejected
Responses:
- Success: OrderResult
- Failure: OrderError
OrderCancel
Cancel an order via WebSocket.
{
"method": "order.cancel",
"id": 11,
"params": {
"tx": "c2lnbmVkX3RyYW5zYWN0aW9uX2J5dGVz"
}
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "order.cancel" or "ORDER.CANCEL" | method: "order.cancel" |
| params.tx | string | yes | signed base64-encoded borsh transaction bytes | (different: orderId/clientOrderId) |
| id | u64 | no | request correlation id | id |
Building the
txpayload: see Cancel Orders for construction and Signing for the sign → encode → submit flow.
Possible errors: ValidationError (empty tx), ServiceUnavailable (submission failed), Timeout, CancelRejected
Responses:
- Success: OrderResult
- Failure: OrderError
OrderAmend
Amend an existing order via WebSocket.
{
"method": "order.amend",
"params": {
"tx": "<signed-base64-borsh-tx>"
},
"id": 7
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "order.amend", "order.modify", or "ORDER.AMEND" | - |
| params.tx | string | yes | signed base64-encoded borsh transaction bytes | - |
| id | u64 | no | request correlation id | id |
Building the
txpayload: see Amend Orders for construction and Signing for the sign → encode → submit flow.
Possible errors: ValidationError (empty tx), ServiceUnavailable (submission failed), Timeout, CancelRejected
Responses:
- Success: OrderResult
- Failure: OrderError
OrderCancelAll
Cancel all open orders for a market via WebSocket.
{
"method": "order.cancelAll",
"params": {
"tx": "<signed-base64-borsh-tx>"
},
"id": 8
}
| Field | Type | Required | Description | Binance Equivalent |
|---|---|---|---|---|
| method | string | yes | "order.cancelAll" or "ORDER.CANCEL_ALL" | - |
| params.tx | string | yes | signed base64-encoded borsh transaction bytes | - |
| id | u64 | no | request correlation id | id |
Building the
txpayload: see Cancel Market Orders / Cancel All Orders for construction and Signing for the sign → encode → submit flow.
Possible errors: ValidationError (empty tx), ServiceUnavailable (submission failed), Timeout, CancelRejected
Responses:
- Success: OrderResult
- Failure: OrderError
Responses
Error
Error response to any client request.
{
"e": "error",
"id": 2,
"E": 1706745600000000,
"error": {
"param": "invalid-topic-format",
"code": -1004,
"msg": "invalid subscription format: expected <symbol>@<stream>"
}
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "error" | - |
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| error.code | i32 | error code | error.code |
| error.msg | string | error message | error.msg |
| error.param | string? | parameter that caused error | - (Bullet-specific) |
OrderResult
Response to OrderPlace, OrderCancel, OrderAmend, or OrderCancelAll.
{
"e": "order.place",
"id": 10,
"E": 1706745600000000,
"results": {
"tx_id": "0xabc123def456",
"status": "processed",
"order_ids": [
1
],
"client_order_ids": [
1
]
}
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "order.place", "order.cancel", "order.amend", "order.cancelAll" | - |
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| results.tx_id | string | transaction hash | - (DEX-specific) |
| results.status | string | tx status — see status values below | - (DEX-specific) |
| results.order_ids | u64[] | affected order ids | - (DEX-specific) |
| results.client_order_ids | u64[] | affected client order ids | - (DEX-specific) |
OrderError
Error response to OrderPlace, OrderCancel, OrderAmend, or OrderCancelAll.
{
"id": 12,
"E": 1706745600000000,
"error": {
"code": -2010,
"msg": "new order rejected: insufficient margin"
}
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| id | u64? | echoed request id | id |
| E | u64 | event time (μs) | - |
| error.code | i32 | error code | error.code |
| error.msg | string | error message | error.msg |
Tx Status Values
| Status | Description |
|---|---|
processed | transaction was executed by the sequencer — check order_ids for affected orders |
published | transaction accepted and will be processed in a subsequent block |
submitted | transaction received by the sequencer but not yet published |
finalized | transaction finalized on-chain |
dropped | transaction was dropped (e.g. expired uniqueness, duplicate generation value) |
unknown | status could not be determined |
Most successful order operations return processed. If you receive dropped, the order was not placed — inspect
the error and retry with a fresh uniqueness value.
Market Data
All market data messages are pushed to subscribed clients. No type wrapper.
DepthUpdate
Topic: SYMBOL@depth, SYMBOL@depth5, SYMBOL@depth10, SYMBOL@depth20, SYMBOL@depth100
{
"e": "depthUpdate",
"E": 1706745600000000,
"T": 1706745600000000,
"s": "BTC-USD",
"U": 1000,
"u": 1000,
"pu": 0,
"b": [
[
"50000.00",
"1.5"
],
[
"49999.00",
"2.0"
]
],
"a": [
[
"50001.00",
"1.2"
],
[
"50002.00",
"3.0"
]
],
"mt": "s"
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "depthUpdate" | e |
| E | u64 | event time (μs) | E |
| T | u64 | transaction time (μs) | T |
| s | string | symbol | s |
| U | u64 | first update id | U |
| u | u64 | last update id (always equals U) | u |
| pu | u64 | previous update id | pu |
| b | [[price, qty], …] | bids (descending) | b |
| a | [[price, qty], …] | asks (ascending) | a |
| mt | string | "s" (snapshot) or "u" (update) | - (Bullet-specific) |
Note: U equals u and as we don’t batch updates.
An update (mt: "u") describes how the top N levels of the subscribed depth changed since the previous message. A level that leaves the top N, including one pushed out by a better price, arrives with qty "0"; a level that enters the top N arrives with its full qty. Changes deeper in the book are not sent. Every depth receives each update, possibly with empty b and a, so its pu chain stays continuous.
AggTrade
Topic: SYMBOL@aggTrade
{
"e": "aggTrade",
"E": 1706745600000000,
"s": "BTC-USD",
"a": 200001,
"p": "50000.50",
"q": "0.5",
"f": 200001,
"l": 200001,
"T": 1706745600000000,
"m": false,
"th": "0xabc123def456",
"ua": "",
"oi": 0,
"mk": false,
"ff": false,
"lq": false,
"fe": "0",
"nf": "0",
"fa": "",
"co": 0,
"sd": "",
"ft": "",
"z": "0",
"Z": "0",
"rs": "0"
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "aggTrade" | e |
| E | u64 | event time (μs) | E |
| s | string | symbol | s |
| a | u64 | aggregate trade id | a |
| p | string | price | p |
| q | string | quantity | q |
| f | u64 | first trade id | f |
| l | u64 | last trade id | l |
| T | u64 | trade time (μs) | T |
| m | bool | is buyer maker | m |
| th | string | transaction hash | - (DEX-specific) |
| ua | string? | deprecated placeholder (""); address not exposed | - (DEX-specific) |
| oi | u64? | deprecated placeholder (0) | - (DEX-specific) |
| mk | bool? | deprecated placeholder (false) | - (DEX-specific) |
| ff | bool? | deprecated placeholder (false) | - (DEX-specific) |
| lq | bool? | deprecated placeholder (false) | - (DEX-specific) |
| fe | string? | deprecated placeholder ("0") | - (DEX-specific) |
| nf | string? | deprecated placeholder ("0"); no Binance equivalent | - (DEX-specific) |
| fa | string? | deprecated placeholder ("") | - (DEX-specific) |
| co | u64? | deprecated placeholder (0); client order id belongs on user order updates | - (DEX-specific) |
| sd | string? | deprecated placeholder (""); infer aggressor side from m | - (DEX-specific) |
| ft | string? | deprecated placeholder (""); fill type belongs on user order updates | - (DEX-specific) |
| z | string? | deprecated placeholder ("0"); cumulative fill data belongs on user order updates | z |
| Z | string? | deprecated placeholder ("0"); cumulative fill data belongs on user order updates | Z |
| rs | string? | deprecated placeholder ("0"); remaining size belongs on user order updates | - (DEX-specific) |
BookTicker
Topic: SYMBOL@bookTicker, !bookTicker
{
"e": "bookTicker",
"u": 1000,
"E": 1706745600000000,
"T": 1706745600000000,
"s": "BTC-USD",
"b": "50000.00",
"B": "1.5",
"a": "50001.00",
"A": "1.2",
"mt": "s"
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "bookTicker" | e |
| u | u64 | update id | u |
| E | u64 | event time (μs) | E |
| T | u64 | transaction time (μs) | T |
| s | string | symbol | s |
| b | string | best bid price | b |
| B | string | best bid qty | B |
| a | string | best ask price | a |
| A | string | best ask qty | A |
| mt | string | "s" (snapshot) or "u" (update) | - (Bullet-specific) |
MarkPrice
Topic: SYMBOL@markPrice, !markPrice@arr
{
"e": "markPriceUpdate",
"E": 1706745600000000,
"s": "BTC-USD",
"p": "50000.50",
"i": "50000.00",
"r": "0.0001",
"T": 1706774400000000
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "markPriceUpdate" | e |
| E | u64 | event time (μs) | E |
| s | string | symbol | s |
| p | string | mark price | p |
| i | string | index price (median CEX price) | i |
| P | string? | estimated settle price | P |
| r | string | funding rate | r |
| T | u64? | next funding time | T |
| th | string? | transaction hash | - (DEX-specific) |
Mark-price E is publication time in Unix microseconds. Oracle and funding
updates can republish the same cached mark with a new E.
Two optional DEX fields identify the actual cached mark event:
| Field | Type | Description |
|---|---|---|
| mark_execution_timestamp_us | u64? | Mark event execution time in Unix microseconds |
| mark_event_number | string? | Exact UInt64 sequencer event number as a decimal string |
These fields and th refer to the mark event, even when an oracle or funding
update triggers publication. Startup REST snapshots and upstream events without
a transaction hash or event number omit the new fields: their mark execution identity is unknown.
Never reinterpret E or the client’s receive time as archived execution time.
To join recent mark history, subscribe and buffer live events before fetching
history. Merge identified events by (th, mark_event_number) (history calls th
txHash), using execution timestamps for ordering and second buckets. Repeated
publications of the same mark add no observation. Take the greatest
(timestampUs, eventNumber, txHash) per UTC second, comparing event
numbers as integers, and retain the winning event’s actual execution timestamp.
Do not shift history to publication time or overwrite a newer live tail with an
older HTTP result. Unknown-identity snapshots may update a current-price label,
but must not seed historical observations. Missing intervals remain gaps; do not
forward-fill them for volatility. Late history can repair older buckets but must
not reset the current live price or replay chart animations.
Liquidation (ForceOrder)
Topic: SYMBOL@liquidations, SYMBOL@forceOrder, !liquidations, !forceOrder, liquidations, forceOrders
{
"e": "liquidation",
"E": 1706745600000000,
"o": {
"s": "BTC-USD",
"S": "SELL",
"o": "LIMIT",
"f": "IOC",
"p": "49000.00",
"ap": "49000.00",
"X": "FILLED",
"l": "1.0",
"T": 1706745600000000,
"th": "0xabc123def456",
"ua": "0xuser123",
"oi": 100001,
"ti": 200001
}
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "liquidation" | e ("forceOrder") |
| E | u64 | event time (μs) | E |
| o.s | string | symbol | o.s |
| o.S | string | side ("BUY" or "SELL") | o.S |
| o.o | string | order type ("LIMIT") | o.o |
| o.f | string | time in force ("IOC") | o.f |
| o.q | string? | original quantity | o.q |
| o.z | string? | filled quantity | o.z |
| o.p | string | price | o.p |
| o.ap | string | average price | o.ap |
| o.X | string | status ("FILLED") | o.X |
| o.l | string | last filled qty | o.l |
| o.T | u64 | trade time (μs) | o.T |
| o.th | string | transaction hash | - (DEX-specific) |
| o.ua | string | liquidated address | - (DEX-specific) |
| o.oi | u64 | order id | - (DEX-specific) |
| o.ti | u64 | trade id | - (DEX-specific) |
Market Status
Topic: marketStatus, marketStatuses, !marketStatus, !marketStatus@arr
Every market’s trading status. On subscribe you get a full snapshot (mt: "s");
after that only what changed (mt: "u"). Each update message carries every
change observed in one refresh, so a single message may list several markets.
The status vocabulary is the same as the status field of
GET /fapi/v1/exchangeInfo, and the feed covers the same symbols
(perpetuals; spot markets are not reported).
{
"e": "marketStatusUpdate",
"E": 1706745600000000,
"mt": "s",
"m": [
{
"s": "BTC-USD",
"i": 1,
"st": "TRADING"
},
{
"s": "ETH-USD",
"i": 2,
"st": "POST_ONLY"
}
]
}
{
"e": "marketStatusUpdate",
"E": 1706745600000000,
"mt": "u",
"m": [
{
"s": "BTC-USD",
"i": 1,
"st": "HALT",
"ps": "TRADING",
"ch": "status"
},
{
"s": "SOL-USD",
"i": 3,
"st": "POST_ONLY",
"ch": "listed"
},
{
"s": "OLD-USD",
"i": 7,
"st": "CLOSE",
"ch": "delisted"
}
]
}
| Field | Type | Description |
|---|---|---|
| e | string | "marketStatusUpdate" |
| E | u64 | event time (μs) |
| mt | string | "s" snapshot (every market) or "u" update (deltas) |
| m | array | the markets reported in this message |
| m[].s | string | symbol |
| m[].i | u16 | market id |
| m[].st | string | status, see below |
| m[].ps | string? | previous status; only on ch: "status" |
| m[].ch | string? | what changed; absent in a snapshot |
Status values (st):
| Value | Description |
|---|---|
TRADING | open for trading |
POST_ONLY | orders may only be posted, not executed; cancels allowed |
CANCEL_ONLY | orders may only be cancelled |
HALT | halted; no trading |
CLOSE | being force-settled or settled, pending deletion |
Change values (ch):
| Value | Description |
|---|---|
listed | a market that was not in the previous view |
status | the market’s reported state changed (see below) |
delisted | the market is gone; st is the status it last had |
ch: "status" covers any change to an existing market’s reported state: its
status, its symbol, or both. ps is the previous status, so ps == st means
only the symbol changed. Every entry carries the market’s current s and st
in full, so applying the entry as-is converges either way.
Deltas are idempotent — applying one twice leaves you in the same state — so a client may safely replay the last message it received.
Notes:
- The snapshot arrives once the server has synced the market registry. A client that subscribes before the very first sync receives nothing until it lands.
- Most status changes are picked up from the rollup as they happen; if that event stream is interrupted, they still arrive on the registry’s periodic refresh.
- One transition is only ever observed on that periodic refresh, because the
rollup emits no event for it: a market moving to
CLOSEduring force-settle.
OrderUpdate
Topic: ADDRESS@user.orders, ADDRESS@ORDER_TRADE_UPDATE
Published for order lifecycle events (NEW, TRADE, CANCELED).
{
"e": "orderTradeUpdate",
"E": 1706745600000000,
"o": {
"s": "BTC-USD",
"i": 100001,
"X": "NEW",
"x": "NEW",
"T": 1706745600000000,
"th": "0xabc123def456",
"ua": "",
"S": "BUY",
"o": "LIMIT",
"f": "GTC",
"p": "50000.00",
"q": "1.0"
}
}
Common fields (all events):
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| e | string | "orderTradeUpdate" | e ("ORDER_TRADE_UPDATE") |
| E | u64 | event time (μs) | E |
| o.s | string | symbol | o.s |
| o.i | u64 | order id | o.i |
| o.X | string | order status | o.X |
| o.x | string | execution type | o.x |
| o.T | u64 | transaction time (μs) | o.T |
| o.th | string | transaction hash | - (DEX-specific) |
| o.ua | string? | deprecated placeholder (""); address implicit from authenticated stream | - (DEX-specific) |
NEW order additional fields:
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| o.S | string | side | o.S |
| o.o | string | order type | o.o |
| o.f | string | time in force | o.f |
| o.p | string | price | o.p |
| o.q | string | quantity | o.q |
TRADE fill additional fields:
{
"e": "orderTradeUpdate",
"E": 1706745600000000,
"o": {
"s": "BTC-USD",
"i": 100001,
"X": "FILLED",
"x": "TRADE",
"T": 1706745600000000,
"th": "0xabc123def456",
"ua": "",
"S": "BUY",
"ap": "50000",
"l": "1.0",
"L": "50000.00",
"n": "0.025",
"N": "USDC",
"m": false,
"isDegen": false,
"t": 200001,
"rp": "10.50",
"ft": "o",
"z": "1.0",
"Z": "50000.00",
"rs": "0"
}
}
| Field | Type | Description | Binance Equivalent |
|---|---|---|---|
| o.S | string | side | o.S |
| o.ap | string? | average price | o.ap |
| o.l | string | last filled qty | o.l |
| o.L | string | last filled price | o.L |
| o.n | string | commission | o.n |
| o.N | string | commission asset | o.N |
| o.m | bool | is maker side | o.m |
| o.isDegen | bool | fill of a Degen position | - (DEX-specific) |
| o.t | u64 | trade id | o.t |
| o.rp | string | realized PnL | o.rp |
| o.ft | string? | fill type (see fill type values) | - (DEX-specific) |
| o.z | string? | cumulative filled size for this order | o.z |
| o.Z | string? | cumulative filled cost (size × price) for this order | o.Z |
| o.rs | string? | remaining size on this order | - (DEX-specific) |
Fill type values (ft):
| Value | Description |
|---|---|
| o | orderbook |
| l | liquidation |
| b | backstop liquidation |
| a | ADL (auto-deleveraging) |
Topics
Symbol-Based Topics
| Bullet Topic | Aliases | Binance Equivalent | Description |
|---|---|---|---|
SYMBOL@depth | symbol@depth | orderbook (default 10) | |
SYMBOL@depth5 | symbol@depth5 | orderbook 5 levels | |
SYMBOL@depth10 | symbol@depth10 | orderbook 10 levels | |
SYMBOL@depth20 | symbol@depth20 | orderbook 20 levels | |
SYMBOL@depth100 | - | orderbook 100 levels | |
SYMBOL@aggTrade | symbol@aggTrade | trades | |
SYMBOL@bookTicker | symbol@bookTicker | best bid/offer | |
SYMBOL@markPrice | symbol@markPrice | mark price + funding | |
SYMBOL@ticker | symbol@ticker | 24hr ticker (coming soon) | |
SYMBOL@liquidations | SYMBOL@forceOrder | symbol@forceOrder | liquidations |
SYMBOL@kline_INTERVAL | symbol@kline_INTERVAL | candlesticks (coming soon) |
Symbol format: Bullet uses BTC-USD (hyphen), Binance uses btcusdt (lowercase, no separator)
Kline intervals: 1m, 5m, 15m, 30m, 1h, 4h, 1d (coming soon)
Parse errors: InvalidFormat, MissingSymbol, InvalidDepth, InvalidInterval, UnknownTopic
Resolution errors: SymbolNotFound
Broadcast Topics
| Bullet Topic | Aliases | Binance Equivalent | Note |
|---|---|---|---|
tickers | !ticker@arr, !ticker | !ticker@arr | coming soon |
markPrices | !markPrice@arr, !markPrice | !markPrice@arr | |
bookTickers | !bookTicker, !bookTicker@arr | !bookTicker | |
liquidations | !liquidations, !forceOrder, forceOrders | !forceOrder@arr | |
marketStatus | marketStatuses, !marketStatus | - | market status |
Parse errors: InvalidFormat, UnknownTopic
User Data Topics
| Bullet Topic | Aliases | Binance Equivalent |
|---|---|---|
ADDRESS@user.orders | ADDRESS@ORDER_TRADE_UPDATE | listenKey stream |
Key difference: Bullet uses address-prefixed topics directly. Binance requires a listenKey from REST API.
Note: Use your main account address, not the delegate address. Order updates for delegate trades are published under the main account.
Resolution errors: MissingUserAddress (when address not provided)
Speed Suffixes
Speed suffixes are accepted but ignored: @100ms, @500ms, @1s
Example: BTC-USD@depth@100ms is equivalent to BTC-USD@depth
Error Codes
General
| Code | Name | Description | Binance Code |
|---|---|---|---|
| -1000 | Unknown | unknown error | -1000 |
| -1001 | Disconnected | server busy/disconnected | -1001 |
| -1002 | Unauthorized | authentication required | -1002 |
| -1003 | TooManyRequests | rate limit exceeded | -1003 |
| -1006 | UnexpectedResponse | unexpected response | -1006 |
| -1007 | Timeout | request timeout | -1007 |
| -1014 | UnknownOrder | order not found | -1014 |
| -1015 | TooManyOrders | order rate limit | -1015 |
| -1016 | ServiceUnavailable | service down | -1016 |
| -1020 | UnsupportedOperation | operation not supported | -1020 |
| -1021 | InvalidTimestamp | bad timestamp | -1021 |
| -1022 | InvalidSignature | signature invalid | -1022 |
Parameters
| Code | Name | Description | Binance Code |
|---|---|---|---|
| -1102 | MandatoryParamMissing | required param missing | -1102 |
| -1111 | BadPrecision | precision error | -1111 |
| -1116 | InvalidOrderType | bad order type | -1116 |
| -1117 | InvalidSide | bad side | -1117 |
| -1122 | InvalidSymbol | invalid symbol | -1122 |
| -1123 | InvalidUserAddress | invalid address | - (Bullet-specific) |
Subscriptions
| Code | Name | Description | Binance Code |
|---|---|---|---|
| -1004 | InvalidSubscriptionFormat | bad topic format | - (Bullet-specific) |
| -1005 | SymbolNotFound | symbol not found | - (Bullet-specific) |
| -1008 | ValidationError | validation failed | - (Bullet-specific) |
| -1010 | SubscriptionExists | already subscribed | - (Bullet-specific) |
Orders
| Code | Name | Description | Binance Code |
|---|---|---|---|
| -2010 | NewOrderRejected | order rejected | -2010 |
| -2011 | CancelRejected | cancel failed | -2011 |
| -2013 | NoSuchOrder | order doesn’t exist | -2013 |
| -2014 | ApiKeyFormatInvalid | bad api key | -2014 |
| -2015 | InvalidApiKeyIpPermissions | auth failure | -2015 |
| -2021 | OrderWouldTrigger | would trigger immediately | -2021 |
Internal
| Code | Name | Description | Binance Code |
|---|---|---|---|
| -4001 | ClientNotFound | client not found | - (internal) |
| -4002 | CouldNotSendMessage | could not send message | - (internal) |
Notes
DEX-Specific Fields
DEX-specific fields use 2-letter codes for compactness:
| Code | Full Name | Description |
|---|---|---|
th | tx_hash | on-chain transaction hash |
ua | user_address | deprecated aggTrade placeholder |
oi | order_id | deprecated aggTrade placeholder |
ti | trade_id | sequencer trade ID |
co | client_order_id | real on order updates; deprecated aggTrade placeholder |
ft | fill_type | Bullet fill provenance |
rs | remaining_size | remaining order size |
nf | net_fee | deprecated aggTrade placeholder |
ff | is_full_fill | deprecated aggTrade placeholder |
lq | is_liquidation | deprecated aggTrade placeholder |
Message Type Field
The mt field in orderbook and BBO messages indicates:
"s"- snapshot (complete state at that depth level)"u"- update (incremental changes since last update)
Response Format Differences
- All responses use
efield for event type (e.g.,"e":"subscribe","e":"error") - Order responses still use Binance-style format with
statuscode - Market data messages use
efield for event type (e.g.,"e":"depthUpdate")