Overview

Disclaimer: Market data provided through this API is for informational purposes only and is delivered "as is" without warranty. It does not constitute financial, investment, or trading advice. See our Terms of Service for full details.

The Qalypto WebSocket API provides real-time, validated market data from multiple cryptocurrency exchanges. Stream trades, tickers, klines, orderbook and liquidation data with sub-second latency.

4
Exchanges
5
Data Types
13
Symbols
~250ms E2E
WS Latency

Supported Exchanges

BinanceBitgetBybitOKX

Data Coverage

Symbols:BTCUSDT, ETHUSDT, SOLUSDT, XRPUSDT, BNBUSDT, DOGEUSDT, ADAUSDT, DOTUSDT, LINKUSDT, AVAXUSDT, LTCUSDT, SUIUSDT, TRXUSDT
Kline Intervals:1m, 5m, 15m, 30m, 1h, 4h, 12h, 1d

Data Delivery Rates

All data is maintained server-side - we handle reconnections, gap detection, and state management. You receive clean, validated data without worrying about exchange quirks.

Data TypeDeliveryNotes
tradeReal-timeEvery trade pushed immediately
tickerReal-timeEvery update pushed immediately
klineReal-timeLive maintained candles with every update
orderbookSampledMaintained L2 snapshot every 0.2s or 100 updates
liquidationReal-timeEvent-driven; legitimately quiet for long stretches

Authentication

Each interface has its own authentication. You will receive your credentials via email within 12 hours of account creation (usually much faster).

ClickHouse (SQL)

Native ClickHouse username + password over HTTPS. No extra headers.

WebSocket - required headers (Cloudflare Access Service Token)

CF-Access-Client-Id: <your-client-id>
CF-Access-Client-Secret: <your-client-secret>

Compliance API

One X-API-Key header per client. See the Compliance API section below.

Keep your credentials secure. Do not share them or commit them to version control. If you believe your credentials have been compromised, contact support immediately.

ClickHouse Connection

Connect to ClickHouse for historical data queries using SQL:

Hostclickhouse.qalypto.com
Port8443 (HTTPS)
Databasemarket_data
Username<your-username>
Password<your-password>

TLS/SSL encryption is required for all connections. Unencrypted connections are not supported.

1import requests
2
3# ClickHouse HTTP API Connection
4HOST = "https://clickhouse.qalypto.com:8443"
5USER = "<your-username>"
6PASSWORD = "<your-password>"
7
8def query(sql):
9 response = requests.get(
10 HOST,
11 params={'query': sql},
12 auth=(USER, PASSWORD),
13 verify=True
14 )
15 response.raise_for_status()
16 return response.text
17
18# Query recent trades
19result = query('''
20 SELECT
21 timestamp,
22 exchange,
23 symbol,
24 price,
25 amount,
26 side
27 FROM market_data.market_data_trade
28 WHERE timestamp >= now() - INTERVAL 1 HOUR
29 ORDER BY timestamp DESC
30 LIMIT 100
31 FORMAT TabSeparated
32''')
33
34for line in result.strip().split('\n'):
35 print(line)

Database Tables

Five tables store all market data. Every table additionally carries the provenance columns consumer_received (DateTime64(3)), producer_received and producer_sent (both Nullable(DateTime64(3))): when we received the event from the exchange and when it was persisted. Historical rows from before mid-2026 honestly carry NULL there.

market_data_trade

ColumnType
timestampDateTime64(3)
exchangeString
symbolString
priceFloat64
amountFloat64
sideString
trade_idString
is_liquidationUInt8

market_data_ticker

ColumnType
timestampDateTime64(3)
exchangeString
symbolString
last_priceFloat64
bid_price, ask_priceNullable(Float64)
bid_size, ask_sizeNullable(Float64)
high_24h, low_24h, volume_24hNullable(Float64)
quote_volume_24hNullable(Float64)
open_24hNullable(Float64)
funding_rateNullable(Float64) - Perpetual funding rate
next_funding_timeNullable(DateTime64) - Next settlement
mark_price, index_priceNullable(Float64)
open_interestNullable(Float64) - Contracts
open_interest_valueNullable(Float64) - USD value

market_data_kline

ColumnType
timestampDateTime64(3)
exchangeString
symbolString
intervalString
open, high, low, closeFloat64
volumeFloat64
quote_volumeNullable(Float64)
num_tradesNullable(Int32)
close_timeNullable(DateTime64(3))
is_finalUInt8

market_data_orderbook

ColumnType
timestampDateTime64(3)
exchangeString
symbolString
bid_prices, bid_sizesArray(Float64)
ask_prices, ask_sizesArray(Float64)
bids_price_0, asks_price_0Float64
bids_size_0, asks_size_0Float64
is_snapshotUInt8
sequence_idString

market_data_liquidation

ColumnType
timestampDateTime64(3)
exchangeString
symbolString
liquidated_sideString (long / short)
priceFloat64
amountFloat64
bankruptcy_priceNullable(Float64)
is_snapshotUInt8

Coverage since 06/2026. Event-driven: long quiet stretches are normal market behaviour, not gaps.

Note on Nullable Fields

Some fields may be NULL depending on the exchange. For example, quote_volume_24h and open_24h are not available from all exchanges.

WebSocket-Only Fields

WebSocket messages include additional fields not present in the ClickHouse tables:

  • _timestamps.websocket_sent - Epoch ms when the WebSocket server dispatched the message to your connection.
  • _timestamps.exchange_event_time - Epoch ms when the event occurred at the exchange (parsed from data.timestamp). Use this for end-to-end latency calculation.
  • producer_received - Top-level epoch ms when our producer first received the data from the exchange.

Latency calculations:

# Internal pipeline latency (producer → websocket server)
internal_latency = _timestamps.websocket_sent - producer_received
# Typical: P50 ~85ms | P95 ~180ms

# End-to-end latency (exchange event → websocket server)
e2e_latency = _timestamps.websocket_sent - _timestamps.exchange_event_time
# Typical: P50 ~180ms | P95 ~400ms (OKX may be higher)

Query Examples

Common SQL queries for market data analysis:

OHLCV Data for Backtesting

Get historical klines
1-- Get OHLCV data for backtesting
2SELECT
3 timestamp,
4 exchange,
5 symbol,
6 interval,
7 open,
8 high,
9 low,
10 close,
11 volume
12FROM market_data.market_data_kline
13WHERE symbol IN ('BTCUSDT', 'ETHUSDT', 'SOLUSDT')
14 AND interval = '1h'
15 AND exchange = 'binance'
16 AND is_final = 1 -- only finalized candles (no live updates)
17 AND timestamp >= now() - INTERVAL 7 DAY
18ORDER BY timestamp ASC

Cross-Exchange Price Comparison

Compare prices across exchanges
1-- Cross-exchange price comparison
2SELECT
3 exchange,
4 avg(last_price) as avg_price,
5 max(last_price) as max_price,
6 min(last_price) as min_price,
7 count() as updates
8FROM market_data.market_data_ticker
9WHERE symbol IN ('BTCUSDT', 'ETHUSDT')
10 AND timestamp >= now() - INTERVAL 5 MINUTE
11GROUP BY exchange, symbol
12ORDER BY avg_price DESC

WebSocket Connection

Connect to the WebSocket API using the following endpoint:

wss://ws.qalypto.com/ws/stream/{client_id}

Parameters

ParameterTypeDescription
client_idstringUnique identifier for your connection (e.g., your username)

Connection Examples

1import asyncio
2import websockets
3import json
4
5async def connect():
6 uri = "wss://ws.qalypto.com/ws/stream/my-client-001"
7 headers = {
8 "CF-Access-Client-Id": "<your-client-id>",
9 "CF-Access-Client-Secret": "<your-client-secret>"
10 }
11
12 async with websockets.connect(uri, extra_headers=headers,
13 ping_interval=None, ping_timeout=None) as ws:
14 # Subscribe to channels
15 await ws.send(json.dumps({
16 "action": "subscribe",
17 "channels": ["trade", "ticker", "kline"]
18 }))
19
20 # Receive messages
21 async for message in ws:
22 msg = json.loads(message)
23 # Handle server heartbeat
24 if msg.get("type") == "ping":
25 await ws.send(json.dumps({"action": "pong"}))
26 continue
27 data = msg.get("data", {})
28 print(f"[{data.get('exchange')}] {data.get('symbol')}: {data}")
29
30asyncio.run(connect())

Connection Heartbeat (Important!)

Similar to Binance, Bybit, and OKX, our WebSocket server implements a heartbeat mechanism to ensure connection stability:

1.
Server sends ping every 20 seconds

You will receive: {"type": "ping", "timestamp": 1706450000000}

2.
Client must respond with pong within 60 seconds

Send: {"action": "pong"}

3.
Connection will be closed if no pong received

After 60 seconds without pong or 3 missed pongs, the server disconnects the client.

# Python example - handle server ping
if msg.get("type") == "ping":
    await ws.send(json.dumps({"action": "pong"}))

Subscription

After connecting, send a subscription message to start receiving data:

Subscribe Message
1{
2 "action": "subscribe",
3 "channels": ["trade", "ticker", "kline", "orderbook", "liquidation"]
4}

Actions

ActionDescription
subscribeSubscribe to one or more channels
unsubscribeUnsubscribe from channels
pingClient-initiated health check (returns pong)
pongResponse to server ping (required for heartbeat)
statusGet connection status
Unsubscribe Message
1{
2 "action": "unsubscribe",
3 "channels": ["orderbook"]
4}

Client → Server Messages

Ping (client-initiated)
1{
2 "action": "ping"
3}
Pong (response to server ping)
1{
2 "action": "pong"
3}
Status Request
1{
2 "action": "status"
3}

Server → Client Messages

Connected (on connection)
1{
2 "type": "connected",
3 "client_id": "my-client-001",
4 "message": "Welcome to Qalypto Real-Time Data Stream",
5 "available_subscriptions": [
6 "trade", "ticker", "orderbook", "kline", "liquidation",
7 "trade:btcusdt", "trade:ethusdt", "ticker:*", "*"
8 ],
9 "connection_guidelines": {
10 "heartbeat": {
11 "ping_interval_seconds": 20,
12 "pong_timeout_seconds": 60,
13 "description": "Server sends ping every 20s. Client must respond with pong within 60s."
14 },
15 "rate_limits": {
16 "max_control_messages_per_second": 5,
17 "max_subscriptions_per_connection": 50
18 },
19 "actions": ["subscribe", "unsubscribe", "ping", "pong", "status"]
20 }
21}
Subscribed (after subscribe)
1{
2 "type": "subscribed",
3 "channels": ["trade", "ticker", "kline", "orderbook", "liquidation"]
4}
Server Ping (Heartbeat)

Server sends this every 20 seconds. You MUST respond with pong!

Server Ping
1{
2 "type": "ping",
3 "timestamp": 1706450000000,
4 "server_time": "2026-01-28T12:53:40.123Z"
5}
Pong Acknowledgement
1{
2 "type": "pong_ack",
3 "timestamp": "2026-01-28T12:53:40.456Z"
4}
Status Response
1{
2 "type": "status",
3 "client_id": "my-client-001",
4 "subscriptions": ["trade", "ticker", "kline", "orderbook", "liquidation"],
5 "messages_received": 12847,
6 "connected_since": "2026-01-28T12:00:00.000Z"
7}
Error Response
1{
2 "error": "Rate limit exceeded. Max 5 control messages per second."
3}

Reconnection Handling

The server does not persist subscriptions after disconnect. Implement automatic reconnection with exponential backoff:

Python - Reconnection with Backoff
1import asyncio
2import websockets
3import json
4from datetime import datetime
5
6class QalyptoClient:
7 def __init__(self, username, cf_client_id, cf_client_secret):
8 self.uri = f"wss://ws.qalypto.com/ws/stream/{username}"
9 self.headers = {
10 "CF-Access-Client-Id": cf_client_id,
11 "CF-Access-Client-Secret": cf_client_secret
12 }
13 self.channels = ["trade", "ticker"]
14 self.reconnect_delay = 1
15 self.max_reconnect_delay = 60
16
17 async def connect(self):
18 while True:
19 try:
20 async with websockets.connect(
21 self.uri,
22 extra_headers=self.headers,
23 ping_interval=None, # Disable library pings - we use server's JSON heartbeat
24 ping_timeout=None
25 ) as ws:
26 print(f"[{datetime.now()}] Connected")
27 self.reconnect_delay = 1 # Reset on success
28
29 # Subscribe
30 await ws.send(json.dumps({
31 "action": "subscribe",
32 "channels": self.channels
33 }))
34
35 async for message in ws:
36 await self.handle_message(json.loads(message), ws)
37
38 except Exception as e:
39 print(f"[{datetime.now()}] Error: {e}")
40 print(f"Reconnecting in {self.reconnect_delay}s...")
41 await asyncio.sleep(self.reconnect_delay)
42 self.reconnect_delay = min(
43 self.reconnect_delay * 2,
44 self.max_reconnect_delay
45 )
46
47 async def handle_message(self, msg, ws):
48 msg_type = msg.get("type")
49
50 # IMPORTANT: Respond to server pings to keep connection alive
51 if msg_type == "ping":
52 await ws.send(json.dumps({"action": "pong"}))
53 return
54
55 # Handle market data
56 data = msg.get("data", {})
57 if msg_type in ["trade", "ticker", "kline", "orderbook", "liquidation"]:
58 print(f"[{msg_type}] {data.get('exchange')}: {data.get('price', data.get('last_price', 'N/A'))}")
59
60# Usage
61client = QalyptoClient("<your-username>", "<your-client-id>", "<your-client-secret>")
62asyncio.run(client.connect())

Channels

Subscribe to different channels to receive specific types of market data:

Data Type Channels

tradeAll trade data from all exchanges
tickerAll ticker data from all exchanges
klineAll kline/candlestick data
orderbookAll orderbook snapshots
liquidationForced liquidations from all exchanges (event-driven, sporadic)

Symbol-Specific Channels

trade:btcusdtTrades for BTCUSDT (works for every symbol: ethusdt, solusdt, xrpusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt)
ticker:ethusdtTicker for ETHUSDT (also: btcusdt, solusdt, xrpusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt)
kline:1m:solusdt1-minute klines for SOLUSDT (any symbol + interval)
orderbook:xrpusdtOrderbook for XRPUSDT (also: btcusdt, ethusdt, solusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt)

Kline Intervals

1m5m15m30m1h4h12h1d

Wildcard

Use * to subscribe to all data types.

Message Format

All messages are JSON objects with the following structure:

Message Structure
1{
2 "type": "trade", // Data type
3 "symbol": "BTCUSDT", // Trading pair
4 "timestamp": "2026-...", // Server timestamp (ISO 8601)
5 "data": { ... }, // Payload
6 "producer_received": 17708... // Epoch ms - when producer received from exchange
7}

Timestamps

All timestamps set by Qalypto are UTC. This includes:

  • producer_receivedEpoch ms (float) - when our producer first received the data from the exchange.
  • _timestamps.websocket_sentEpoch ms (int) - when the WebSocket server dispatched the message to your connection.
  • _timestamps.exchange_event_timeEpoch ms (int) - when the event occurred at the exchange. Use for end-to-end latency.

Internal latency: websocket_sent - producer_received (P50 ~85ms)

E2E latency: websocket_sent - exchange_event_time (P50 ~180ms)

Compliance API - Overview & Auth

The Compliance API is the evidence layer on top of the data platform: market data in the regulator's format, case dossiers, best-execution evidence and cryptographic proofs down to the Bitcoin anchor. It is delivery-audited: every response and every denied attempt is logged with a content hash.

Authentication
1# Base URL
2https://compliance.qalypto.com
3
4# Authentication: one header, per-client key
5curl -H "X-API-Key: <your-key>" \
6 "https://compliance.qalypto.com/esma/meta"

Good to know

  • Personalization: keys configured with your GLEIF LEI (checksum-verified) emit it as the executing party in every ESMA record.
  • Proof headers: responses for sealed days carry X-Qalypto-Super-Root - the day's Bitcoin-anchored Merkle root.
  • Honest scope: GET /esma/meta documents what we deliberately do not offer (auth.116/118 order registers cannot be derived from public data) and why.

ESMA Records (auth.117)

Verified trades as schema-valid ISO 20022 CryptoAssetRecordKeepingReport documents (ESMA MiCA package, validated against the official schema at build time). Uselimit / offset to page large days;X-Qalypto-More tells you when to keep going.

GET /esma/auth117
1curl -H "X-API-Key: <your-key>" \
2 "https://compliance.qalypto.com/esma/auth117?venue=binance&symbol=BTCUSDT&date=2026-07-07&limit=1000"
3
4# Response (trimmed): one Tx/New record per trade
5{
6 "Document": { "CrptAsstRcrdKeepgRpt": { "Tx": [ {
7 "New": {
8 "TxRcrdId": "8395015042",
9 "ExctgPty": { "LEI": "<your LEI, when configured>" },
10 "Tx": { "TradDt": "2026-07-07T00:00:03.510Z",
11 "Qty": { "Unit": "0.25" }, "TradVn": "BINC" },
12 "CrptAsst": { "DgtlTknIdr": { "DTI": "4H95J0R2X" } }
13 } } ] } }
14}

Case Dossiers

The audit annex as one ZIP: a window (up to 7 days, one or more symbols) with all five channels as CSV, the same trades as auth.117 JSON per venue, the exact quality-check and gap-ledger evidence of that window, and the proof anchors. The Merkle chain verifies fully offline. Any row cap that bites is declared indossier.json - never silent.

GET /dossier
1curl -OJ -H "X-API-Key: <your-key>" \
2 "https://compliance.qalypto.com/dossier?symbol=BTCUSDT,ETHUSDT&from=2026-07-07&to=2026-07-07"
3
4qalypto-dossier-2symbols-2026-07-07.zip
5├── data/ trades, klines_final, orderbook_top, ticker, liquidations (CSV)
6├── esma/ auth117-<venue>.json (personalized, schema-valid)
7├── evidence/ quality_checks.csv, data_gaps.csv (exactly this window)
8├── proofs/ registry entries + anchors-<day>-r<rev>.json (RFC-3161 + Bitcoin)
9├── dossier.json manifest: row counts, truncation flags, sealed days
10└── README.md how to verify offline

Best Execution

Answers the examiner's question "was your price in the market?" for one moment: the best bid/ask of all four venues at the timestamp plus trade statistics of the surrounding window - from sealed data.

GET /bestex
1curl -H "X-API-Key: <your-key>" \
2 "https://compliance.qalypto.com/bestex?symbol=BTCUSDT&ts=2026-07-07 14:02:07&window_s=60"
3
4{
5 "venues": {
6 "binance": { "trades_in_window": 21120, "low": 62960.0, "high": 63115.0,
7 "vwap": 63028.26, "touch": { "best_bid": 63027.0, "best_ask": 63027.1 } },
8 "bybit": { "...": "..." }
9 },
10 "consolidated": { "best_bid": 63027.0, "best_ask": 63027.1 },
11 "how_to_read": "An execution at or inside the consolidated touch was in the market."
12}

Proofs & Anchors

Prove a single row up to the Bitcoin-anchored super-root, and fetch the anchor blobs for self-service offline verification. The anchors response uses the open-source verifier's field names - it feeds straight in, no renaming.

Inclusion proofs and anchors
1# 1) Request an inclusion proof for one trade (async job)
2curl -X POST -H "X-API-Key: <your-key>" \
3 "https://compliance.qalypto.com/proof/inclusion?day=2026-07-07&venue=bitget&channel=trade&symbol=LTCUSDT&trade_id=1458176976084537344"
4# -> { "job_id": "ca1998aa98a7427a", "poll": "/proof/inclusion/ca1998aa98a7427a" }
5
6# 2) Poll, save the proof, verify OFFLINE
7curl -H "X-API-Key: <your-key>" \
8 "https://compliance.qalypto.com/proof/inclusion/ca1998aa98a7427a" > proof.json
9python3 qalypto_verify.py inclusion --proof proof.json
10# RESULT: VALID
11
12# 3) The day's anchors (RFC-3161 token + OpenTimestamps proof)
13curl -H "X-API-Key: <your-key>" \
14 "https://compliance.qalypto.com/proof/anchors?day=2026-07-07" > anchors.json
15python3 qalypto_verify.py ots --registry anchors.json
16# with a local Bitcoin node: fully trustless verification
17# without one: 'ots info' names the block, any explorer confirms the header

The verifier (qalypto_verify.py, MIT, standard library only) recomputes every hash locally. It is open source at github.com/Qalypto/verifier and ships inside every dossier ZIP. No Qalypto system needs to be online - or trusted - for verification. Concepts and the honest scope boundary: the compliance model.

Trade Data

Trade messages contain individual trade executions:

Trade Message
1{
2 "type": "trade",
3 "symbol": "SOLUSDT",
4 "timestamp": "2026-03-02T14:01:45.300327",
5 "data": {
6 "_timestamps": {
7 "websocket_sent": 1772460105301,
8 "exchange_event_time": 1772460105100
9 },
10 "timestamp": "2026-03-02 14:01:45.100000",
11 "exchange": "binance",
12 "symbol": "SOLUSDT",
13 "price": 82.98,
14 "amount": 0.75,
15 "side": "sell",
16 "trade_id": "3198735563",
17 "is_liquidation": false
18 },
19 "producer_received": 1772460105224.0476
20}

Ticker Data

Ticker messages contain current price and volume information:

Ticker Message
1{
2 "type": "ticker",
3 "symbol": "SOLUSDT",
4 "timestamp": "2026-03-02T14:01:45.309347",
5 "data": {
6 "_timestamps": {
7 "websocket_sent": 1772460105313,
8 "exchange_event_time": 1772460105138
9 },
10 "timestamp": "2026-03-02 14:01:45.138000",
11 "exchange": "bybit",
12 "symbol": "SOLUSDT",
13 "last_price": 82.98,
14 "bid_price": 82.98,
15 "ask_price": 82.99,
16 "bid_size": 312.7,
17 "ask_size": 455.4,
18 "high_24h": 86.84,
19 "low_24h": 81.63,
20 "volume_24h": 15414549.2,
21 "quote_volume_24h": 1292880761.203,
22 "open_24h": null,
23 "funding_rate": -5.129e-05,
24 "next_funding_time": "2026-03-02 16:00:00+00:00",
25 "mark_price": 82.989,
26 "index_price": 83.051,
27 "open_interest": 6893141.2,
28 "open_interest_value": null
29 },
30 "producer_received": 1772460105223.4756
31}

Kline Data

Kline (candlestick) messages contain OHLCV data. Note: interval is also available as a top-level field for convenient filtering without parsing data.

Kline Message
1{
2 "type": "kline",
3 "symbol": "DOGEUSDT",
4 "timestamp": "2026-03-02T14:01:45.299305",
5 "data": {
6 "_timestamps": {
7 "websocket_sent": 1772460105300,
8 "exchange_event_time": 1772460000000
9 },
10 "timestamp": "2026-03-02 14:00:00",
11 "exchange": "binance",
12 "symbol": "DOGEUSDT",
13 "interval": "1h",
14 "open": 0.0909,
15 "high": 0.09093,
16 "low": 0.09083,
17 "close": 0.09083,
18 "volume": 4784283.0,
19 "quote_volume": 434786.04922,
20 "num_trades": 1314,
21 "close_time": "2026-03-02 14:59:59.999000",
22 "is_final": false
23 },
24 "interval": "1h",
25 "producer_received": 1772460105128.6987
26}

Orderbook Data

Orderbook messages contain bid/ask depth:

Orderbook Message
1{
2 "type": "orderbook",
3 "symbol": "XRPUSDT",
4 "timestamp": "2026-03-02T14:01:45.275758",
5 "data": {
6 "_timestamps": {
7 "websocket_sent": 1772460105301,
8 "exchange_event_time": 1772460105156
9 },
10 "timestamp": "2026-03-02 14:01:45.156000",
11 "exchange": "bybit",
12 "symbol": "XRPUSDT",
13 "bids": [
14 [1.3388, 1000.0],
15 [1.3387, 2500.0],
16 [1.3386, 1800.0]
17 ],
18 "asks": [
19 [1.3389, 800.0],
20 [1.3390, 1500.0],
21 [1.3391, 2200.0]
22 ],
23 "is_snapshot": true,
24 "sequence_id": "233916094713"
25 },
26 "producer_received": 1772460105157.5078
27}

Liquidation Data

Liquidation messages report forced position closures on the exchange. liquidated_side is the side of the position that was liquidated (a liquidated short produces a forced buy). bankruptcy_price is only provided by some venues and is null otherwise. Liquidations are sparse events: expect seconds to minutes between messages, not a continuous stream.

Liquidation Message
1{
2 "type": "liquidation",
3 "symbol": "ETHUSDT",
4 "timestamp": "2026-07-14T11:02:35.912467",
5 "data": {
6 "_timestamps": {
7 "websocket_sent": 1784027755914,
8 "exchange_event_time": 1784027755759
9 },
10 "timestamp": "2026-07-14 11:02:35.759000",
11 "exchange": "bybit",
12 "symbol": "ETHUSDT",
13 "liquidated_side": "short",
14 "price": 1804.99,
15 "amount": 0.24,
16 "bankruptcy_price": 1804.99,
17 "is_snapshot": false
18 },
19 "producer_received": 1784027755803.2841
20}

Best Practices

Heartbeat (Critical!)

  • Always respond to server pings with pong - Connection will be closed after 60s without response
  • When you receive {"type": "ping"}, immediately send {"action": "pong"}
  • Do NOT rely on library-level ping/pong - our server uses JSON messages
  • Server sends ping every 20 seconds

Connection Management

  • Use a single WebSocket connection per application
  • Implement automatic reconnection with exponential backoff (1s, 2s, 4s, 8s...)
  • Handle connection drops gracefully - re-subscribe after reconnect
  • Store your subscriptions locally to restore them after reconnect
  • Server does not persist subscriptions - always re-subscribe after reconnect

Performance & Backpressure

  • Subscribe only to the channels you need - avoid wildcard (*) in production
  • Use symbol-specific channels to reduce data volume
  • Process messages asynchronously - never block the WebSocket receive loop
  • Use a message queue if processing is slow - drop old messages if queue is full
  • Monitor your processing latency - if you fall behind, you may get disconnected
  • Expected throughput: 500-800 msg/s depending on market activity

Error Handling

  • Handle {"error": "..."} messages gracefully
  • Rate limit errors: Back off and retry after 1 second
  • Connection closed unexpectedly: Reconnect with exponential backoff
  • Invalid JSON: Log and skip the message, don't crash
  • Unknown message types: Ignore them - we may add new types in the future

Security

  • Never expose your credentials in client-side code
  • Use environment variables for credentials
  • Rotate credentials if compromised
  • Use unique client_id per connection for debugging

Rate Limits

Current rate limits for the WebSocket API:

Connections per accountConfigured per contract (contact sales)
Subscriptions per connection50
Control messages per second5
Server ping interval20 seconds
Pong timeout (disconnect)60 seconds
Max connection durationNo limit

Control messages include subscribe, unsubscribe, ping, pong, and status requests. Need higher limits? Contact us at info@qalypto.com

© 2026 Qalypto. All rights reserved.

Questions? Contact us at info@qalypto.com