WebSocket
A WebSocket connection is provided to stream brokerage prices as executable quotes through our Request-For-Stream (RFS) service
WebSocket API - RFS (Request for Stream)
Section titled “WebSocket API - RFS (Request for Stream)”The WebSocket API provides real-time streaming prices and instant order execution through Zodia Markets’ Request for Stream (RFS) service. Unlike REST API’s request-response pattern, WebSocket maintains a persistent connection for continuous price updates.
What You Can Do
Section titled “What You Can Do”With the WebSocket API, you can:
- Stream live prices for multiple currency pairs simultaneously
- Execute trades instantly on displayed prices
- Subscribe/unsubscribe to price streams dynamically
- Specify beneficiary on subscription For quotes on delivering crypto to third party wallets
Environment URLs
Section titled “Environment URLs”Connect to the appropriate environment for your integration stage:
Sandbox:
wss://trade-uk.sandbox.zodiamarkets.comProduction:
wss://trade-uk.zodiamarkets.comAuthentication Overview
Section titled “Authentication Overview”WebSocket connections require token-based authentication:
Step 1: Obtain WebSocket Token
Section titled “Step 1: Obtain WebSocket Token”First, request a WebSocket authentication token via REST API:
token_response = make_api_request('POST', 'api/3/zm/rest/auth/token', {})ws_token = token_response['token']const tokenResponse = await makeApiRequest('POST', 'api/3/zm/rest/auth/token', {});const wsToken = tokenResponse.token;The token is valid for a limited time (typically 1 hour).
Step 2: Connect with Token
Section titled “Step 2: Connect with Token”Use the token to establish your WebSocket connection.
wss://trade-uk.zodiamarkets.com/zm/ws/ws-client?token={token}Step 3 (optional): Name a Session
Section titled “Step 3 (optional): Name a Session”Add a sessionId to bind order outcomes to a session rather than to this connection, so an outcome survives a dropped connection and is redelivered when you reconnect.
wss://trade-uk.zodiamarkets.com/zm/ws/ws-client?token={token}&sessionId={sessionId}Supply it if you place orders with autoSubscribe: true; omit it otherwise. A malformed value refuses the connection, and reconnecting under a sessionId that is already live for your account closes the earlier connection — see Order Sessions and Recovery →.
Service Availability
Section titled “Service Availability”Trading Hours
Section titled “Trading Hours”The RFS WebSocket service operates during:
Monday to Friday: 04:00 GMT to 22:00 GMT, including public holidays
For trading outside these hours, contact the Zodia Markets OTC Brokerage desk:
Maintenance Windows
Section titled “Maintenance Windows”Scheduled maintenance is announced in advance on the Status Page. Typical maintenance windows:
- Daily: 21:00 GMT - 21:15 GMT
Connection Lifecycle
Section titled “Connection Lifecycle”1. Obtain Token (REST API) ↓2. Connect to WebSocket ↓3. Authenticate with Token ↓4. Subscribe to Price Streams ↓5. Receive Price Updates ↓6. Execute Orders (Optional) ↓7. Disconnect or Token ExpiresConnection Best Practices
Section titled “Connection Best Practices”Handling Disconnections
Section titled “Handling Disconnections”WebSocket connections can disconnect for various reasons. Implement automatic reconnection:
import time
def connect_with_retry(ws_url, max_retries=5): """Connect with exponential backoff retry""" for attempt in range(max_retries): try: # Get fresh token token_response = make_api_request('POST', 'api/3/zm/rest/auth/token', {}) ws_token = token_response['token']
# Connect and authenticate ws = create_connection(ws_url) authenticate(ws, ws_token)
return ws
except Exception as e: if attempt < max_retries - 1: wait_time = 2 ** attempt print(f"Connection failed. Retrying in {wait_time}s...") time.sleep(wait_time) else: raiseasync function connectWithRetry(wsUrl, maxRetries = 5) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { // Get fresh token const tokenResponse = await makeApiRequest('POST', 'api/3/zm/rest/auth/token', {}); const wsToken = tokenResponse.token;
// Connect and authenticate return new Promise((resolve, reject) => { const ws = new WebSocket(wsUrl);
ws.on('open', () => { authenticate(ws, wsToken); resolve(ws); });
ws.on('error', reject); });
} catch (error) { if (attempt < maxRetries - 1) { const waitTime = Math.pow(2, attempt) * 1000; console.log(`Connection failed. Retrying in ${waitTime}ms...`); await new Promise(resolve => setTimeout(resolve, waitTime)); } else { throw error; } } }}Token Expiry Handling
Section titled “Token Expiry Handling”WebSocket tokens expire after a set period. Monitor for expiry and refresh:
def monitor_connection(ws): """Monitor connection and handle token expiry""" while True: try: message = json.loads(ws.recv())
if message.get('error') == 'expired token': print("Token expired. Reconnecting...") ws.close() ws = connect_with_retry(ws_url) resubscribe_to_streams(ws)
else: handle_message(message)
except Exception as e: print(f"Connection error: {e}") ws = connect_with_retry(ws_url) resubscribe_to_streams(ws)function monitorConnection(ws) { ws.on('message', async (data) => { const message = JSON.parse(data);
if (message.error === 'expired token') { console.log('Token expired. Reconnecting...'); ws.close(); ws = await connectWithRetry(wsUrl); resubscribeToStreams(ws); } else { handleMessage(message); } });
ws.on('close', async () => { console.log('Connection closed. Reconnecting...'); ws = await connectWithRetry(wsUrl); resubscribeToStreams(ws); });}