WebSocket API - Getting Started
Welcome to the Hercle WebSocket API documentation. This guide will help you establish a real-time SignalR connection and start receiving live market data.
Overview
The Hercle WebSocket API uses SignalR technology for real-time, bidirectional communication. SignalR provides reliable connections with automatic reconnection, message buffering, and support for multiple transport protocols.
Why SignalR?
- Automatic reconnection - Handles network interruptions gracefully
- Message ordering - Guarantees message delivery order
- Type safety - Strongly typed hub methods and responses
- Wide language support - Client libraries for JavaScript, C#, Python, Java, and more
Connection URLs
- Production:
https://publicapi.hercle.financial/ExecutionLiveServer/v1 - Sandbox:
https://publicapi.sandbox.hercle.financial/ExecutionLiveServer/v1
Prerequisites
Before you begin, ensure you have:
- API Key - Get your key from the Account page after KYC approval
- SignalR Client Library - Install for your programming language (see below)
Installing SignalR Client Libraries
JavaScript (npm):
npm install @microsoft/signalr
Python (pip):
pip install signalrcore
C# (NuGet):
Install-Package Microsoft.AspNetCore.SignalR.Client
Java (Maven):
<dependency>
<groupId>com.microsoft.signalr</groupId>
<artifactId>signalr</artifactId>
<version>7.0.0</version>
</dependency>
Quick Start Guide
Step 1: Establish SignalR Connection
Create a hub connection with your API key:
JavaScript:
const signalR = require('@microsoft/signalr');
const apiKey = 'YOUR_API_KEY';
const baseUrl = 'https://publicapi.sandbox.hercle.financial';
const hubPath = 'ExecutionLiveServer/v1';
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${baseUrl}/${hubPath}`, {
accessTokenFactory: () => apiKey,
})
.withAutomaticReconnect()
.build();
connection
.start()
.then(() => console.log('Connected to Hercle SignalR hub'))
.catch((err) => console.error('Connection failed:', err));
Python:
from signalrcore.hub_connection_builder import HubConnectionBuilder
api_key = 'YOUR_API_KEY'
base_url = 'https://publicapi.sandbox.hercle.financial'
hub_path = 'ExecutionLiveServer/v1'
connection = HubConnectionBuilder()\
.with_url(f"{base_url}/{hub_path}",
options={"access_token_factory": lambda: api_key})\
.with_automatic_reconnect({
"type": "interval",
"intervals": [0, 2000, 10000, 30000]
})\
.build()
connection.start()
View full Authentication documentation →
Step 2: Listen for Events
Set up event listeners before starting the connection:
// Listen for execution events (orders, trades, balances)
connection.on('executionEvents', (message) => {
const response = JSON.parse(message);
console.log('Event received:', response.subject);
const data = JSON.parse(response.data);
console.log('Data:', data);
});
// Listen for subscription events (price updates)
connection.on('subscriptions', (message) => {
const response = JSON.parse(message);
if (response.subject === 'execution.feeds.priceUpdate') {
const priceData = JSON.parse(response.data);
console.log(
`${priceData.pair}: Buy ${priceData.buyPrice}, Sell ${priceData.sellPrice}`
);
}
});
Step 3: Send Commands
Once connected, invoke hub methods to interact with the API:
Get Available Trading Pairs:
connection
.invoke('GetPairs', ['my-client-123'])
.catch((err) => console.error('GetPairs failed:', err));
Subscribe to Price Updates:
connection
.invoke('SubscribePairPriceChannel', ['my-client-123', 'BTCUSDT', 1.0])
.catch((err) => console.error('Subscribe failed:', err));
Place an Order:
connection
.invoke('PlaceOtcOrder', [
'my-order-123', // ClientId
'price-update-id', // PriceId from price subscription
'buy', // Side (buy or sell)
1.0, // Slippage (1.0 = 1%)
])
.catch((err) => console.error('Order failed:', err));
Browse all WebSocket methods →
Complete Working Example
Here's a complete example that connects, subscribes to prices, and places an order:
const signalR = require('@microsoft/signalr');
const apiKey = 'YOUR_API_KEY';
const baseUrl = 'https://publicapi.sandbox.hercle.financial';
const hubPath = 'ExecutionLiveServer/v1';
// Step 1: Create connection
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${baseUrl}/${hubPath}`, {
accessTokenFactory: () => apiKey,
})
.withAutomaticReconnect()
.build();
let currentPriceId = null;
// Step 2: Set up event listeners
connection.on('executionEvents', (message) => {
const response = JSON.parse(message);
console.log('Execution event:', response.subject);
// Handle different event types
if (response.subject === 'execution.pairs') {
const pairs = JSON.parse(response.data);
console.log('Available pairs:', pairs);
}
if (response.subject === 'execution.order.executed') {
const order = JSON.parse(response.data);
console.log('Order executed:', order);
}
// Check for errors
if (
response.subject === 'preChecksError' ||
response.subject.endsWith('.error')
) {
const errorData = JSON.parse(response.data);
console.error('Error:', errorData);
}
});
connection.on('subscriptions', (message) => {
const response = JSON.parse(message);
// Handle price updates
if (response.subject === 'execution.feeds.priceUpdate') {
const priceData = JSON.parse(response.data);
currentPriceId = priceData.id;
console.log(`Price Update for ${priceData.pair}:`);
console.log(` Buy: ${priceData.buyPrice}`);
console.log(` Sell: ${priceData.sellPrice}`);
console.log(` Mark: ${priceData.markPrice}`);
console.log(` Price ID: ${priceData.id}`);
}
});
// Step 3: Start connection and execute commands
connection
.start()
.then(async () => {
console.log('Connected to Hercle SignalR hub');
// Get available pairs
await connection.invoke('GetPairs', ['my-client-123']);
// Subscribe to BTC price updates
await connection.invoke('SubscribePairPriceChannel', [
'my-client-123',
'BTCUSDT',
0.01, // Size: 0.01 BTC
]);
console.log('Subscribed to BTCUSDT price feed');
// Wait for price updates, then place order
setTimeout(async () => {
if (currentPriceId) {
console.log('Placing order with price ID:', currentPriceId);
await connection.invoke('PlaceOtcOrder', [
'my-order-123',
currentPriceId,
'buy',
1.0, // 1% slippage
]);
}
}, 5000);
})
.catch((err) => console.error('Connection error:', err));
// Handle reconnection
connection.onreconnecting((error) => {
console.log('Connection lost, reconnecting...', error);
});
connection.onreconnected((connectionId) => {
console.log('Reconnected! Connection ID:', connectionId);
// Re-subscribe to price feeds after reconnection
connection.invoke('SubscribePairPriceChannel', [
'my-client-123',
'BTCUSDT',
0.01,
]);
});
connection.onclose((error) => {
console.log('Connection closed', error);
});
Hub Response Structure
All hub responses follow this format:
{
"channel": "eventName",
"topic": "topicName",
"subject": "topic.subject",
"type": "message",
"clientId": "clientId",
"data": "{...}"
}
Response Fields
channel- The event channel name (executionEventsorsubscriptions)topic- Event topic (e.g.,Account,Execution)subject- Specific subject indicating the event type (e.g.,execution.pairs,execution.order.executed)type- Message type (alwaysmessage)clientId- Your custom client IDdata- JSON string containing the response data (must be parsed)
Common Subjects
Successful Responses:
execution.pairs- Available trading pairsexecution.order.executed- Order execution resultexecution.feeds.priceUpdate- Real-time price updateaccount.balances.snapshot- Balance informationaccount.orders.snapshot- Order historyaccount.trades.snapshot- Trade history
Error Responses:
preChecksError- Request validation failed*.error- General operation error (checkdata.statusErrorordata.message)
View full error documentation →
Available Methods
General Methods
Query market data and system information:
- GetPairs - List all available trading pairs
- GetAssetNetworks - Get supported networks for an asset
- GetPairSizeByAmount - Calculate pair size based on amount
Subscription Methods
Subscribe to real-time data feeds:
- SubscribePairPriceChannel - Subscribe to price updates
- UnsubscribePairPriceChannel - Unsubscribe from price updates
Trading Methods
Execute trades and manage orders:
- PlaceOtcOrder - Place an order using a price ID
- GetOrdersPaginated - Retrieve order history
- GetTradesPaginated - Retrieve trade history
- CreateRequestForQuote - Create an RFQ
- AcceptRequestForQuote - Accept an RFQ
Funding Methods
Manage balances and withdrawals:
- GetBalances - Get account balances
- WithdrawFunds - Create a withdrawal
- GetWithdrawalsPaginated - Retrieve withdrawal history
- GetWhitelistedAddresses - List whitelisted addresses
- WhitelistAddress - Add a new whitelisted address
- CreateDepositAddress - Generate a deposit address
- GetDepositAddress - List deposit addresses
Banking Methods
Manage payees and transfers:
- GetPayeesPaginated - List payees
- RegisterPayee - Add a new payee
- GetPayeeAddressesPaginated - List payee addresses
- RegisterPayeeAddress - Add a payee address
- TransferFunds - Initiate a transfer
- GetTransfersPaginated - Retrieve transfer history
Lightning Network Methods
Bitcoin Lightning Network operations:
- CreateLightningInvoice - Generate a Lightning invoice
- GetLightningInvoices - List Lightning invoices
- WithdrawLightning - Withdraw via Lightning Network
Connection Management
Automatic Reconnection
SignalR provides automatic reconnection out of the box:
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${baseUrl}/${hubPath}`, {
accessTokenFactory: () => apiKey,
})
.withAutomaticReconnect() // Default: [0, 2000, 10000, 30000] ms delays
.build();
Custom Reconnection Policy:
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: (retryContext) => {
// Exponential backoff with max delay
const delay = Math.min(1000 * Math.pow(2, retryContext.previousRetryCount), 30000);
console.log(`Reconnect attempt ${retryContext.previousRetryCount + 1}, waiting ${delay}ms`);
return delay;
}
})
Handling Connection State
Monitor connection state changes:
connection.onreconnecting((error) => {
console.log('Connection lost, reconnecting...', error);
// Update UI to show "connecting" state
});
connection.onreconnected((connectionId) => {
console.log('Reconnected! Connection ID:', connectionId);
// Update UI to show "connected" state
// Re-subscribe to channels
resubscribeToChannels();
});
connection.onclose((error) => {
console.error('Connection closed:', error);
// Update UI to show "disconnected" state
// Optionally attempt manual reconnection
});
Resubscribing After Reconnection
Save your active subscriptions and restore them after reconnection:
const activeSubscriptions = new Map();
async function subscribeToPriceChannel(clientId, pair, size) {
await connection.invoke('SubscribePairPriceChannel', [clientId, pair, size]);
activeSubscriptions.set(`${pair}-${size}`, { clientId, pair, size });
}
async function resubscribeToChannels() {
for (const [key, sub] of activeSubscriptions) {
console.log(`Resubscribing to ${sub.pair}`);
await connection.invoke('SubscribePairPriceChannel', [
sub.clientId,
sub.pair,
sub.size,
]);
}
}
connection.onreconnected(resubscribeToChannels);
Maintenance Windows
HercleX has scheduled maintenance that may cause disconnections:
- Regular Maintenance: Every Wednesday, 10:00 AM - 11:00 AM GMT+1
- Urgent Upgrades: Communicated in advance when possible
Your application should:
- Implement automatic reconnection (enabled by default with SignalR)
- Re-subscribe to channels after reconnection
- Handle temporary service unavailability gracefully
Check the status page for current system status.
Error Handling
Parse Error Responses
Check the subject field to detect errors:
connection.on('executionEvents', (message) => {
const response = JSON.parse(message);
// Check for pre-check validation errors
if (response.subject === 'preChecksError') {
const errorData = JSON.parse(response.data);
console.error('Validation error:', errorData.message);
// Handle validation error
return;
}
// Check for general operation errors
if (response.subject.endsWith('.error')) {
const errorData = JSON.parse(response.data);
console.error(
'Operation error:',
errorData.message || errorData.statusError
);
// Handle operation error
return;
}
// Process successful response
const data = JSON.parse(response.data);
// Handle data...
});
Handle Order Status
Orders may be rejected even with valid responses:
connection.on('executionEvents', (message) => {
const response = JSON.parse(message);
if (response.subject === 'execution.order.executed') {
const order = JSON.parse(response.data);
if (order.status === 1) {
console.log('Order executed successfully:', order);
} else if (order.status === 2) {
console.error('Order cancelled/rejected:', order.statusError);
// Handle order rejection (e.g., insufficient funds)
}
}
});
View complete error documentation →
Best Practices
- Use Automatic Reconnection - Enable
.withAutomaticReconnect()for reliable connections - Parse Responses Carefully - All
datafields are JSON strings that must be parsed - Check Subject Fields - Always verify the subject to detect errors
- Handle Order Status - Check the
statusfield in order responses (0=created, 1=executed, 2=cancelled) - Resubscribe After Reconnection - Maintain a list of active subscriptions and restore them
- Validate Before Sending - Check argument types and ranges to avoid
preChecksError - Monitor Connection State - Use connection events to update UI and handle disconnections
- Use ClientId for Tracking - Provide unique ClientIds to track specific requests
Next Steps
→ Authentication
Learn how to securely authenticate with SignalR
→ Price Subscriptions
Subscribe to real-time price feeds
→ Place Orders
Execute trades using the WebSocket API
→ Error Handling
Understand SignalR error patterns and handling
Additional Resources
- SignalR Documentation: Microsoft SignalR Overview
- Connection Lifetime: Handling Connection Events
- API Status: status.hercle.financial
- All WebSocket Methods: Browse Methods
Need Help?
-
Browse the WebSocket Methods for detailed command documentation
-
Check Error Handling for common issues and solutions
-
Visit the API Status Page for system status
-
Review the changelog for API updates