WebSocket Guide
This page covers connection lifecycle, heartbeat, reconnection strategy, and best practices that apply to all BTSE WebSocket endpoints (Spot, Futures, OTC).
For product-specific topics and subscription details, see the WebSocket API section in each product's sidebar.
Endpointsβ
| Product | Production | Testnet |
|---|---|---|
| Spot WebSocket | wss://ws.btse.com/ws/spot | wss://testws.btse.io/ws/spot |
| Spot OSS | wss://ws.btse.com/ws/oss/spot | wss://testws.btse.io/ws/oss/spot |
| Futures WebSocket | wss://ws.btse.com/ws/futures | wss://testws.btse.io/ws/futures |
| Futures OSS | wss://ws.btse.com/ws/oss/futures | wss://testws.btse.io/ws/oss/futures |
| Otc WebSocket | wss://ws.btse.com/ws/otc | wss://testws.btse.io/ws/otc |
| Otc OSS | wss://ws.btse.com/ws/oss/otc | wss://testws.btse.io/ws/oss/otc |
Main vs OSS: The OSS (Order Stream Service) endpoint is a dedicated, high-throughput channel for orderbook data only. Use the main endpoint for everything else (trades, authentication, notifications, fills).
Connection Lifecycleβ
Connect β (optional) Authenticate β Subscribe β Receive messages
β |
βββββββββββββ ping every 15s to keep alive βββββββββββ
- Connect β open a WebSocket to the appropriate endpoint
- Authenticate (private topics only) β send
authKeyExpireswith HMAC-SHA384 signature - Subscribe β send
{"op": "subscribe", "args": ["topic1", "topic2"]} - Receive β messages arrive as JSON, except
pongwhich is plain text - Keep alive β send
ping(plain text) at regular intervals
Heartbeat (Ping / Pong)β
Send a plain-text ping message to keep the connection alive. The server responds with pong.
| Parameter | Value |
|---|---|
| Recommended interval | Every 15 seconds |
| Server timeout | Connections idle for 60 seconds without a ping are dropped |
| Format | Plain text ping (not JSON) |
β ping
β pong
If you miss the pong response, your connection may be stale β close and reconnect.
Reconnection Strategyβ
Connections can drop due to network issues, server maintenance, or idle timeouts. Your client should handle disconnects gracefully:
Recommended approachβ
- Detect disconnect β listen for WebSocket
closeanderrorevents - Wait before reconnecting β use exponential backoff:
- 1st retry: 1 second
- 2nd retry: 2 seconds
- 3rd retry: 4 seconds
- Max: 30 seconds between retries
- Re-authenticate β authentication does not persist across connections
- Re-subscribe β subscriptions do not persist across connections
- Resync state β for orderbook streams, the first message after re-subscribe is always a full
snapshot
Orderbook-specific recoveryβ
If you detect any of these conditions, unsubscribe and re-subscribe to get a fresh snapshot:
- Sequence gap β
seqNum!=prevSeqNum + 1 - Crossed orderbook β best bid >= best ask
- Stale data β no update received for an extended period
Connection Limitsβ
| Limit | Value |
|---|---|
| Max connections per IP | 50 |
| Max subscriptions per connection | 100 topics |
| Message buffer | If the server's outbound buffer to your client fills up, the connection is closed (error code 1007) |
To stay within limits:
- Share a single connection for multiple topics when possible
- Unsubscribe from topics you no longer need
- Use OSS endpoints for orderbook data to keep the main connection lighter
Authenticationβ
Private topics (notifications, fills, positions) require authenticating the WebSocket session. Authentication is per-connection and does not expire until the connection closes.
Signature: HMAC-SHA384(secret, wsPath + nonce)
Where wsPath is the WebSocket path (e.g. /ws/spot, /ws/futures).
{
"op": "authKeyExpires",
"args": ["<API_KEY>", "<NONCE>", "<SIGNATURE>"]
}
See Quickstart β Step 4 for complete code examples.
Subscription Formatβ
All topics use the same subscribe/unsubscribe format:
{"op": "subscribe", "args": ["topic1", "topic2"]}
{"op": "unsubscribe", "args": ["topic1"]}
The server acknowledges with:
{"event": "subscribe", "channel": ["topic1", "topic2"]}
Available Topicsβ
Spot (wss://ws.btse.com/ws/spot)β
| Topic | Auth | Description |
|---|---|---|
tradeHistoryApi:<symbol> | Public | Real-time trade feed |
notificationApiV3 | Private | Order status updates |
fills | Private | Trade fill notifications |
Spot OSS (wss://ws.btse.com/ws/oss/spot)β
| Topic | Auth | Description |
|---|---|---|
snapshotL1:<symbol> | Public | Best bid/ask (Level 1) |
update:<symbol>_<grouping> | Public | Orderbook incremental updates (snapshot + delta) |
Futures (wss://ws.btse.com/ws/futures)β
| Topic | Auth | Description |
|---|---|---|
tradeHistoryApiV3:<symbol> | Public | Real-time trade feed |
notificationApiV4 | Private | Order status updates |
fillsV2 | Private | Trade fill notifications |
allPositionV4 | Private | All positions snapshot |
positionsV3 | Private | Position updates |
Futures OSS (wss://ws.btse.com/ws/oss/futures)β
| Topic | Auth | Description |
|---|---|---|
snapshotL1:<symbol>_<grouping> | Public | Best bid/ask by grouping |
update:<symbol>_<grouping> | Public | Orderbook incremental updates |
OTC (wss://ws.btse.com/ws/otc)β
| Topic | Auth | Description |
|---|---|---|
quote | Private | Real-time OTC quote stream |
Error Codes (WebSocket)β
| Code | Message | Action |
|---|---|---|
1000 | Market pair not supported | Check symbol name |
1001 | Operation not supported | Check op field |
1002 | Invalid request | Check required fields |
1005 | Topic does not exist | Check topic name format |
1007 | Message buffer full | Reduce subscription count or consume faster |
1008 | Max failed attempts reached | Session closed β reconnect |