Appearance
WebSocket
Connection
URL: wss://api-v1-ab.blink.trade/v1/ws
Keepalive requirements
Clients must keep the connection alive by sending at least one frame every 2 minutes. This can be either:
- A WebSocket ping frame
- Any application-level message
If the server receives no frames from a client within the two-minute window, it closes the connection. Slow consumers may be disconnected.
Handshake
The server sends the protocol version and keepalive contract when the connection opens:
json
{ "type": "hello", "protocol_version": 1, "keepalive_timeout_ms": 120000 }Channels
A connection can have at most one subscription per channel. Application-frame timestamps use Unix nanoseconds encoded as decimal strings.
Stateful channels include previous_sequence and sequence. The first update has previous_sequence: null. Before applying a later update, verify that its previous_sequence equals the last installed sequence for that channel. Sequence values are strictly increasing but not necessarily consecutive.
Markets
markets
Subscribe
json
{ "type": "subscribe", "channel": "markets" }Updates
The complete active market catalog is sent after subscribing:
json
{
"type": "update",
"channel": "markets",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"markets": [
{
"market_type": "perp",
"market_id": 1,
"symbol": "BTC-USD",
"base_token": "BTC",
"quote_token": "USDC",
"quote_token_id": "token1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxv8v5",
"base_decimals": 8,
"quote_decimals": 8,
"base_atoms_per_base_lot": "100",
"quote_atoms_per_quote_lot": "1",
"base_lots_per_base_unit": "1000000",
"quote_lots_per_quote_unit": "100000000",
"quote_lots_per_base_unit_per_tick": "1000000",
"default_maker_fee_100th_bps": 0,
"default_taker_fee_100th_bps": 200,
"liquidation_fee_100th_bps": 500,
"min_size_base_lots": "1",
"min_order_notional_quote_lots": "1000000",
"max_market_order_value_quote_lots": "100000000000",
"max_limit_order_value_quote_lots": "200000000000",
"max_leverage": 20,
"oracle_id": "BTC-USD",
"fee_recipient": "11111111111111111111111111111111",
"margin_tiers": [
{
"tier": 0,
"notional_lower_bound_quote_lots": "0",
"max_leverage": 20,
"maintenance_margin_rate_bps": 250,
"maintenance_deduction_quote_lots": "0"
}
],
"status": "active"
}
]
}Each update contains the complete market catalog.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "markets" }Market Statistics
market-stats/{market_type}market-stats/{market_type}/{market_id}
The market-type channel covers every active market of that type. The market-specific channel covers one market.
Subscribe
json
{ "type": "subscribe", "channel": "market-stats/perp/1" }Updates
The current statistics are sent after subscribing:
json
{
"type": "update",
"channel": "market-stats/perp/1",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"market_stats": [
{
"market_type": "perp",
"market_id": 1,
"timestamp_ns": "1785441661123456789",
"last_price_ticks": "6500000",
"mark_price_ticks": "6500100",
"oracle_price_ticks": "6500200",
"mid_price_ticks": "6500050",
"best_bid_price_ticks": "6500000",
"best_ask_price_ticks": "6500100",
"open_interest_base_lots": "123456",
"funding_rate_100th_bps": 125,
"predicted_funding_rate_100th_bps": 140,
"volume_24h_base_lots": "987654",
"volume_24h_quote_lots": "64197510",
"trades_24h": "3200",
"low_24h_price_ticks": "6400000",
"high_24h_price_ticks": "6600000",
"change_24h_bps": 150
}
]
}Each update contains the complete selected statistics set. Spot rows omit mark price, open interest, leverage, and funding fields.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "market-stats/perp/1" }Best Bid and Offer
bbo/{market_type}/{market_id}
Subscribe
json
{ "type": "subscribe", "channel": "bbo/perp/1" }Updates
The current BBO is sent after subscribing. Updates are sent when the best price or size changes:
json
{
"type": "update",
"channel": "bbo/perp/1",
"timestamp_ns": "1785441661123456789",
"bbo": {
"bid": { "price_ticks": "6500000", "size_base_lots": "12" },
"ask": { "price_ticks": "6500100", "size_base_lots": "8" }
}
}bid and ask are null when that side of the book is empty.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "bbo/perp/1" }Order Book
orderbook/{market_type}/{market_id}
Subscribe
json
{ "type": "subscribe", "channel": "orderbook/perp/1" }Updates
The current order book is sent after subscribing:
json
{
"type": "update",
"channel": "orderbook/perp/1",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"orderbook": {
"bids": [
{ "price_ticks": "6500000", "size_base_lots": "12" },
{ "price_ticks": "6499900", "size_base_lots": "6" }
],
"asks": [
{ "price_ticks": "6500100", "size_base_lots": "8" },
{ "price_ticks": "6500200", "size_base_lots": "4" }
]
}
}Later updates contain changed levels with their absolute sizes:
json
{
"type": "update",
"channel": "orderbook/perp/1",
"timestamp_ns": "1785441662123456789",
"previous_sequence": "812",
"sequence": "819",
"orderbook": {
"bids": [
{ "price_ticks": "6500000", "size_base_lots": "15" }
],
"asks": [
{ "price_ticks": "6500100", "size_base_lots": "0" }
]
}
}A size of 0 removes the level. Before applying an update, clients must verify that previous_sequence matches the last installed sequence. On mismatch, discard the local book and subscribe again.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "orderbook/perp/1" }Trades
trades/{market_type}/{market_id}
Subscribe
json
{ "type": "subscribe", "channel": "trades/perp/1" }Updates
Up to 100 recent trades are sent after subscribing, ordered oldest to newest:
json
{
"type": "update",
"channel": "trades/perp/1",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"trades": [
{
"timestamp_ns": "1785441660123456789",
"price_ticks": "6500000",
"size_base_lots": "4",
"taker_is_bid": true
}
]
}Later frames contain every trade for the market from one transaction:
json
{
"type": "update",
"channel": "trades/perp/1",
"timestamp_ns": "1785441662123456789",
"previous_sequence": "812",
"sequence": "819",
"trades": [
{
"timestamp_ns": "1785441662123456789",
"price_ticks": "6500100",
"size_base_lots": "2",
"taker_is_bid": false
}
]
}Before applying an update, clients must verify that previous_sequence matches the last installed sequence. On mismatch, discard the local trade collection and subscribe again. If no trade history is available, trades is empty and sequence is null.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "trades/perp/1" }Trade Candles
candles/{market_type}/{market_id}/{resolution}
Subscribe
json
{ "type": "subscribe", "channel": "candles/perp/1/1m" }Updates
The latest trade candle is sent after subscribing. candles is empty if no trade candle is available:
json
{
"type": "update",
"channel": "candles/perp/1/1m",
"timestamp_ns": "1785441660123456789",
"candles": [
{
"open_time_ms": 1785441600000,
"open": "6500000",
"high": "6500100",
"low": "6499900",
"close": "6500000",
"volume_base": "12",
"volume_quote": "78000000",
"trades": 3
}
]
}Updates contain complete candle records identified by open_time_ms:
json
{
"type": "update",
"channel": "candles/perp/1/1m",
"timestamp_ns": "1785441660123456789",
"candles": [
{
"open_time_ms": 1785441600000,
"open": "6500000",
"high": "6500100",
"low": "6499900",
"close": "6500000",
"volume_base": "12",
"volume_quote": "78000000",
"trades": 3
},
{
"open_time_ms": 1785441660000,
"open": "6500200",
"high": "6500200",
"low": "6500200",
"close": "6500200",
"volume_base": "1",
"volume_quote": "6500200",
"trades": 1
}
]
}The first update for a new bucket contains the finalized previous trade candle and the new trade candle. Quiet buckets are not emitted.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "candles/perp/1/1m" }Mark Price Candles
mark-candles/{market_id}/{resolution}
Subscribe
json
{ "type": "subscribe", "channel": "mark-candles/1/1m" }Updates
The latest mark price candle is sent after subscribing. candles is empty if no mark price candle is available:
json
{
"type": "update",
"channel": "mark-candles/1/1m",
"timestamp_ns": "1785441660123456789",
"candles": [
{
"open_time_ms": 1785441600000,
"open": "6500000",
"high": "6500100",
"low": "6499900",
"close": "6500000",
"samples": 4
}
]
}Updates contain complete candle records identified by open_time_ms:
json
{
"type": "update",
"channel": "mark-candles/1/1m",
"timestamp_ns": "1785441660123456789",
"candles": [
{
"open_time_ms": 1785441600000,
"open": "6500000",
"high": "6500100",
"low": "6499900",
"close": "6500000",
"samples": 4
},
{
"open_time_ms": 1785441660000,
"open": "6500200",
"high": "6500200",
"low": "6500200",
"close": "6500200",
"samples": 1
}
]
}The first update for a new bucket contains the finalized previous mark price candle and the new mark price candle. Quiet buckets are not emitted.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "mark-candles/1/1m" }Funding Rates
funding-rates/{market_id}
Subscribe
json
{ "type": "subscribe", "channel": "funding-rates/1" }Updates
The 200 most recent funding rows are sent after subscribing, ordered oldest to newest:
json
{
"type": "update",
"channel": "funding-rates/1",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"funding_rates": [
{
"timestamp_ns": "1785438000000000000",
"sequence": "800",
"market_id": 1,
"funding_rate_100th_bps": 25,
"long_cumulative_funding_quote_lots": "1234",
"short_cumulative_funding_quote_lots": "-1234"
}
]
}Each update is a complete snapshot of the 200 most recent funding rows.
Unsubscribe
json
{ "type": "unsubscribe", "channel": "funding-rates/1" }Account Catalog
accounts/{address}
Subscribe
json
{
"type": "subscribe",
"channel": "accounts/11111111111111111111111111111111"
}Updates
The complete subaccount and delegation catalog is sent after subscribing:
json
{
"type": "update",
"channel": "accounts/11111111111111111111111111111111",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"subaccounts": [
{
"subaccount_id": 0,
"status": "active",
"created_at_ns": "1785000000000000000"
}
],
"delegations": [
{
"subaccount_id": 0,
"delegate": "11111111111111111111111111111111",
"permissions": ["place_orders", "cancel_orders"],
"expires_at_ns": "1786046460123000000",
"name": "trading key"
}
]
}Each update is a complete snapshot of the subaccount and delegation catalog.
Unsubscribe
json
{
"type": "unsubscribe",
"channel": "accounts/11111111111111111111111111111111"
}Account State
account/{address}/{subaccount_id}
Subscribe
json
{
"type": "subscribe",
"channel": "account/11111111111111111111111111111111/0"
}Updates
The first update contains complete current account, balance, position, leverage, and order state. It also contains up to 200 recent fills and funding payments:
json
{
"type": "update",
"channel": "account/11111111111111111111111111111111/0",
"timestamp_ns": "1785441661123456789",
"previous_sequence": null,
"sequence": "812",
"account": {
"subaccount_id": 0,
"status": "active",
"created_at_ns": "1785000000000000000",
"cross_margin_balance_quote_lots": "900000",
"total_isolated_margin_quote_lots": "100000",
"equity_quote_lots": "1000000",
"available_margin_quote_lots": "800000",
"transferable_margin_quote_lots": "750000",
"initial_margin_requirement_quote_lots": "150000",
"maintenance_margin_requirement_quote_lots": "75000",
"delegations": []
},
"balances": [
{
"token_id": "token1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxv8v5",
"total_token_atoms": "100000000",
"locked_token_atoms": "25000000",
"deposited_token_atoms": "150000000",
"withdrawn_token_atoms": "50000000"
}
],
"positions": [],
"leverages": [],
"orders": [],
"fills": [],
"funding_payments": []
}Each update is a complete snapshot of current account state, including up to 200 recent fills and funding payments.
Unsubscribe
json
{
"type": "unsubscribe",
"channel": "account/11111111111111111111111111111111/0"
}Error codes
| Code | Description |
|---|---|
400 | Invalid command or channel |
404 | Resource not found |
409 | Channel already subscribed |
503 | Stream unavailable |
After a connection closes, reconnect and subscribe again. See Errors for retry behavior.
The frame schema is available in the v1 AsyncAPI specification.