Skip to content

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/account

The 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/trade

The 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:

HeaderDescription
X-VALR-API-KEYYour API key
X-VALR-SIGNATUREHMAC-SHA512 signature
X-VALR-TIMESTAMPUnix 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": { ... }
}