Overview
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.
Supported Exchanges
Data Coverage
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 Type | Delivery | Notes |
|---|---|---|
| trade | Real-time | Every trade pushed immediately |
| ticker | Real-time | Every update pushed immediately |
| kline | Real-time | Live maintained candles with every update |
| orderbook | Sampled | Maintained L2 snapshot every 0.2s or 100 updates |
| liquidation | Real-time | Event-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)
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:
| Host | clickhouse.qalypto.com |
| Port | 8443 (HTTPS) |
| Database | market_data |
| Username | <your-username> |
| Password | <your-password> |
TLS/SSL encryption is required for all connections. Unencrypted connections are not supported.
1import requests23# ClickHouse HTTP API Connection4HOST = "https://clickhouse.qalypto.com:8443"5USER = "<your-username>"6PASSWORD = "<your-password>"78def query(sql):9 response = requests.get(10 HOST,11 params={'query': sql},12 auth=(USER, PASSWORD),13 verify=True14 )15 response.raise_for_status()16 return response.text1718# Query recent trades19result = query('''20 SELECT21 timestamp,22 exchange,23 symbol,24 price,25 amount,26 side27 FROM market_data.market_data_trade28 WHERE timestamp >= now() - INTERVAL 1 HOUR29 ORDER BY timestamp DESC30 LIMIT 10031 FORMAT TabSeparated32''')3334for 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
| Column | Type |
|---|---|
| timestamp | DateTime64(3) |
| exchange | String |
| symbol | String |
| price | Float64 |
| amount | Float64 |
| side | String |
| trade_id | String |
| is_liquidation | UInt8 |
market_data_ticker
| Column | Type |
|---|---|
| timestamp | DateTime64(3) |
| exchange | String |
| symbol | String |
| last_price | Float64 |
| bid_price, ask_price | Nullable(Float64) |
| bid_size, ask_size | Nullable(Float64) |
| high_24h, low_24h, volume_24h | Nullable(Float64) |
| quote_volume_24h | Nullable(Float64) |
| open_24h | Nullable(Float64) |
| funding_rate | Nullable(Float64) - Perpetual funding rate |
| next_funding_time | Nullable(DateTime64) - Next settlement |
| mark_price, index_price | Nullable(Float64) |
| open_interest | Nullable(Float64) - Contracts |
| open_interest_value | Nullable(Float64) - USD value |
market_data_kline
| Column | Type |
|---|---|
| timestamp | DateTime64(3) |
| exchange | String |
| symbol | String |
| interval | String |
| open, high, low, close | Float64 |
| volume | Float64 |
| quote_volume | Nullable(Float64) |
| num_trades | Nullable(Int32) |
| close_time | Nullable(DateTime64(3)) |
| is_final | UInt8 |
market_data_orderbook
| Column | Type |
|---|---|
| timestamp | DateTime64(3) |
| exchange | String |
| symbol | String |
| bid_prices, bid_sizes | Array(Float64) |
| ask_prices, ask_sizes | Array(Float64) |
| bids_price_0, asks_price_0 | Float64 |
| bids_size_0, asks_size_0 | Float64 |
| is_snapshot | UInt8 |
| sequence_id | String |
market_data_liquidation
| Column | Type |
|---|---|
| timestamp | DateTime64(3) |
| exchange | String |
| symbol | String |
| liquidated_side | String (long / short) |
| price | Float64 |
| amount | Float64 |
| bankruptcy_price | Nullable(Float64) |
| is_snapshot | UInt8 |
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 fromdata.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
1-- Get OHLCV data for backtesting2SELECT3 timestamp,4 exchange,5 symbol,6 interval,7 open,8 high,9 low,10 close,11 volume12FROM market_data.market_data_kline13WHERE 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 DAY18ORDER BY timestamp ASC
Cross-Exchange Price Comparison
1-- Cross-exchange price comparison2SELECT3 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 updates8FROM market_data.market_data_ticker9WHERE symbol IN ('BTCUSDT', 'ETHUSDT')10 AND timestamp >= now() - INTERVAL 5 MINUTE11GROUP BY exchange, symbol12ORDER BY avg_price DESC
WebSocket Connection
Connect to the WebSocket API using the following endpoint:
Parameters
| Parameter | Type | Description |
|---|---|---|
| client_id | string | Unique identifier for your connection (e.g., your username) |
Connection Examples
1import asyncio2import websockets3import json45async 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 }1112 async with websockets.connect(uri, extra_headers=headers,13 ping_interval=None, ping_timeout=None) as ws:14 # Subscribe to channels15 await ws.send(json.dumps({16 "action": "subscribe",17 "channels": ["trade", "ticker", "kline"]18 }))1920 # Receive messages21 async for message in ws:22 msg = json.loads(message)23 # Handle server heartbeat24 if msg.get("type") == "ping":25 await ws.send(json.dumps({"action": "pong"}))26 continue27 data = msg.get("data", {})28 print(f"[{data.get('exchange')}] {data.get('symbol')}: {data}")2930asyncio.run(connect())
Connection Heartbeat (Important!)
Similar to Binance, Bybit, and OKX, our WebSocket server implements a heartbeat mechanism to ensure connection stability:
You will receive: {"type": "ping", "timestamp": 1706450000000}
Send: {"action": "pong"}
After 60 seconds without pong or 3 missed pongs, the server disconnects the client.
if msg.get("type") == "ping":
await ws.send(json.dumps({"action": "pong"}))
Subscription
After connecting, send a subscription message to start receiving data:
1{2 "action": "subscribe",3 "channels": ["trade", "ticker", "kline", "orderbook", "liquidation"]4}
Actions
| Action | Description |
|---|---|
| subscribe | Subscribe to one or more channels |
| unsubscribe | Unsubscribe from channels |
| ping | Client-initiated health check (returns pong) |
| pong | Response to server ping (required for heartbeat) |
| status | Get connection status |
1{2 "action": "unsubscribe",3 "channels": ["orderbook"]4}
Client → Server Messages
1{2 "action": "ping"3}
1{2 "action": "pong"3}
1{2 "action": "status"3}
Server → Client Messages
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": 5018 },19 "actions": ["subscribe", "unsubscribe", "ping", "pong", "status"]20 }21}
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!
1{2 "type": "ping",3 "timestamp": 1706450000000,4 "server_time": "2026-01-28T12:53:40.123Z"5}
1{2 "type": "pong_ack",3 "timestamp": "2026-01-28T12:53:40.456Z"4}
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}
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:
1import asyncio2import websockets3import json4from datetime import datetime56class 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_secret12 }13 self.channels = ["trade", "ticker"]14 self.reconnect_delay = 115 self.max_reconnect_delay = 601617 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 heartbeat24 ping_timeout=None25 ) as ws:26 print(f"[{datetime.now()}] Connected")27 self.reconnect_delay = 1 # Reset on success2829 # Subscribe30 await ws.send(json.dumps({31 "action": "subscribe",32 "channels": self.channels33 }))3435 async for message in ws:36 await self.handle_message(json.loads(message), ws)3738 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_delay45 )4647 async def handle_message(self, msg, ws):48 msg_type = msg.get("type")4950 # IMPORTANT: Respond to server pings to keep connection alive51 if msg_type == "ping":52 await ws.send(json.dumps({"action": "pong"}))53 return5455 # Handle market data56 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'))}")5960# Usage61client = 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
| trade | All trade data from all exchanges |
| ticker | All ticker data from all exchanges |
| kline | All kline/candlestick data |
| orderbook | All orderbook snapshots |
| liquidation | Forced liquidations from all exchanges (event-driven, sporadic) |
Symbol-Specific Channels
| trade:btcusdt | Trades for BTCUSDT (works for every symbol: ethusdt, solusdt, xrpusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt) |
| ticker:ethusdt | Ticker for ETHUSDT (also: btcusdt, solusdt, xrpusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt) |
| kline:1m:solusdt | 1-minute klines for SOLUSDT (any symbol + interval) |
| orderbook:xrpusdt | Orderbook for XRPUSDT (also: btcusdt, ethusdt, solusdt, bnbusdt, dogeusdt, adausdt, dotusdt, linkusdt, avaxusdt, ltcusdt, suiusdt, trxusdt) |
Kline Intervals
Wildcard
Use * to subscribe to all data types.
Message Format
All messages are JSON objects with the following structure:
1{2 "type": "trade", // Data type3 "symbol": "BTCUSDT", // Trading pair4 "timestamp": "2026-...", // Server timestamp (ISO 8601)5 "data": { ... }, // Payload6 "producer_received": 17708... // Epoch ms - when producer received from exchange7}
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.
1# Base URL2https://compliance.qalypto.com34# Authentication: one header, per-client key5curl -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/metadocuments 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.
1curl -H "X-API-Key: <your-key>" \2 "https://compliance.qalypto.com/esma/auth117?venue=binance&symbol=BTCUSDT&date=2026-07-07&limit=1000"34# Response (trimmed): one Tx/New record per trade5{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.
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"34qalypto-dossier-2symbols-2026-07-07.zip5├── 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 days10└── 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.
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"34{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.
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" }56# 2) Poll, save the proof, verify OFFLINE7curl -H "X-API-Key: <your-key>" \8 "https://compliance.qalypto.com/proof/inclusion/ca1998aa98a7427a" > proof.json9python3 qalypto_verify.py inclusion --proof proof.json10# RESULT: VALID1112# 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.json15python3 qalypto_verify.py ots --registry anchors.json16# with a local Bitcoin node: fully trustless verification17# 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:
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": 17724601051009 },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": false18 },19 "producer_received": 1772460105224.047620}
Ticker Data
Ticker messages contain current price and volume information:
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": 17724601051389 },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": null29 },30 "producer_received": 1772460105223.475631}
Kline Data
Kline (candlestick) messages contain OHLCV data. Note: interval is also available as a top-level field for convenient filtering without parsing data.
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": 17724600000009 },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": false23 },24 "interval": "1h",25 "producer_received": 1772460105128.698726}
Orderbook Data
Orderbook messages contain bid/ask depth:
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": 17724601051569 },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.507827}
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.
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": 17840277557599 },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": false18 },19 "producer_received": 1784027755803.284120}
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 account | Configured per contract (contact sales) |
| Subscriptions per connection | 50 |
| Control messages per second | 5 |
| Server ping interval | 20 seconds |
| Pong timeout (disconnect) | 60 seconds |
| Max connection duration | No 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