Appearance
WebSocket API
VALR provides real-time WebSocket APIs for streaming account updates and market data. WebSocket connections offer lower latency than polling REST endpoints and enable bidirectional communication for order placement.
Channels
VALR operates two WebSocket channels, each serving a distinct purpose:
Account Channel
wss://api.valr.com/ws/accountThe Account channel provides authenticated, account-specific events and commands:
- Balance updates — real-time notification when any currency balance changes
- Order lifecycle events — order status changes, processing results, and failures
- Trade notifications — new trades on your account
- Transaction history — deposits, withdrawals, fees, and rewards
- Order commands — place, cancel, and modify orders via WebSocket for lower latency
🔐 Authentication Required
The Account channel requires authentication. All events and commands require a valid API key.
Trade Channel
wss://api.valr.com/ws/tradeThe Trade channel provides real-time market data:
- Order book updates — aggregated order book with price levels
- New trades — live trade feed with price, quantity, and taker side
- Market summaries — bid/ask prices, volume, and 24-hour statistics
💡 No Auth Needed
The Trade channel supports unauthenticated connections. You can receive market data without API credentials. Note that unauthenticated connections are automatically disconnected after 15 minutes.
Authentication
WebSocket connections are authenticated using HMAC-based API key credentials. Two methods are available:
Header-based Authentication
Authentication is performed by passing HTTP headers during the WebSocket handshake. The following headers are required:
| Header | Description |
|---|---|
X-VALR-API-KEY | Your API key |
X-VALR-SIGNATURE | HMAC-SHA512 signature |
X-VALR-TIMESTAMP | Unix timestamp in milliseconds |
The signature is computed as:
Signature = HMAC-SHA512(API_SECRET, TIMESTAMP + "GET" + "/ws/account")NodeJS example:
javascript
const crypto = require('crypto');
const WebSocket = require('ws');
function signRequest(apiSecret, timestamp, verb, path) {
return crypto
.createHmac('sha512', apiSecret)
.update(timestamp + verb + path)
.digest('hex');
}
function getAuthHeaders(apiKey, apiSecret, path) {
const timestamp = Date.now().toString();
const signature = signRequest(apiSecret, timestamp, 'GET', path);
return {
'X-VALR-API-KEY': apiKey,
'X-VALR-SIGNATURE': signature,
'X-VALR-TIMESTAMP': timestamp,
};
}
const headers = getAuthHeaders('YOUR_API_KEY', 'YOUR_API_SECRET', '/ws/account');
const ws = new WebSocket('wss://api.valr.com/ws/account', { headers });
ws.on('open', () => {
console.log('Connected and authenticated');
});
ws.on('message', (data) => {
console.log('Received:', JSON.parse(data));
});In-band Authentication (Alternative)
Alternatively, you can connect first and then send an authentication message in-band:
json
{
"type": "AUTHENTICATE",
"payload": {
"apiKey": "YOUR_API_KEY",
"signature": "SIGNATURE",
"timestamp": 1234567890123
}
}The signature is computed the same way as for header-based authentication.
For detailed authentication instructions and code examples, see the Authentication guide.
Ping-Pong Keepalive
To maintain an active connection, clients must send a PING message at least every 30 seconds. VALR will respond with a PONG message.
Send:
json
{
"type": "PING"
}Response:
json
{
"type": "PONG"
}TIP
On the Trade WebSocket, the PONG response body will be "NO_SUBSCRIPTIONS" if you are not currently subscribed to any message types.
Subscribing to Events
Some events on the Account channel are automatically delivered upon connection. Others require explicit subscription, as do all Trade channel events.
To subscribe, send a message with the event names you wish to receive:
json
{
"type": "SUBSCRIBE",
"subscriptions": [
{ "event": "ORDER_STATUS_UPDATE" },
{
"event": "AGGREGATED_ORDERBOOK_UPDATE",
"pairs": ["BTCZAR", "ETHZAR"]
}
]
}The optional pairs filter limits Trade channel events to specific currency pairs. If omitted, you receive events for all pairs.
Unsubscribing
Account channel — send an UNSUBSCRIBE message:
json
{
"type": "UNSUBSCRIBE",
"requests": [
{ "event": "ORDER_STATUS_UPDATE" }
]
}Trade channel — send a SUBSCRIBE message with an empty pairs array for the event you wish to unsubscribe from:
json
{
"type": "SUBSCRIBE",
"subscriptions": [
{
"event": "AGGREGATED_ORDERBOOK_UPDATE",
"pairs": []
}
]
}Message Format
All server-to-client messages follow an envelope structure:
Account channel:
json
{
"type": "EVENT_TYPE",
"data": { ... }
}Trade channel:
json
{
"type": "EVENT_TYPE",
"currencyPairSymbol": "BTCZAR",
"data": { ... }
}