Hercle

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:

  1. API Key - Get your key from the Account page after KYC approval
  2. 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 (executionEvents or subscriptions)
  • topic - Event topic (e.g., Account, Execution)
  • subject - Specific subject indicating the event type (e.g., execution.pairs, execution.order.executed)
  • type - Message type (always message)
  • clientId - Your custom client ID
  • data - JSON string containing the response data (must be parsed)

Common Subjects

Successful Responses:

  • execution.pairs - Available trading pairs
  • execution.order.executed - Order execution result
  • execution.feeds.priceUpdate - Real-time price update
  • account.balances.snapshot - Balance information
  • account.orders.snapshot - Order history
  • account.trades.snapshot - Trade history

Error Responses:

  • preChecksError - Request validation failed
  • *.error - General operation error (check data.statusError or data.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

Browse all methods →

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:

  1. Implement automatic reconnection (enabled by default with SignalR)
  2. Re-subscribe to channels after reconnection
  3. 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

  1. Use Automatic Reconnection - Enable .withAutomaticReconnect() for reliable connections
  2. Parse Responses Carefully - All data fields are JSON strings that must be parsed
  3. Check Subject Fields - Always verify the subject to detect errors
  4. Handle Order Status - Check the status field in order responses (0=created, 1=executed, 2=cancelled)
  5. Resubscribe After Reconnection - Maintain a list of active subscriptions and restore them
  6. Validate Before Sending - Check argument types and ranges to avoid preChecksError
  7. Monitor Connection State - Use connection events to update UI and handle disconnections
  8. Use ClientId for Tracking - Provide unique ClientIds to track specific requests

Next Steps

Additional Resources

Need Help?