Skip to main content
Connection URLs, protocol requirements, rate limits, and reconnection strategy for the XOXNO Relayer.

Connection URLs

EndpointProtocolPurpose
wss://relayer.xoxno.com/wsWebSocket (TLS)Full-duplex: subscribe, relay, broadcast
wss://relayer.xoxno.com/ws/statsWebSocket (TLS)Read-only network stats stream (no subscribe needed)
https://relayer.xoxno.comHTTPSREST API base URL
All WebSocket messages are JSON. The Relayer accepts binary frames for proto broadcast passthrough.

Limits at a glance

ParameterValue
Max topics per connection64
Rate limit (control messages)64 per 5-second window
Max message size4 MB
Ping/pong heartbeat interval30 seconds
Max subscribers per address topic100
Max unique address entries10,000

Ping/pong heartbeat

The Relayer sends a WebSocket Ping frame every 30 seconds. Your client must respond with a Pong frame to keep the connection alive. Most WebSocket libraries handle this automatically. If the Relayer receives no Pong response, it closes the connection. If your library does not auto-respond to pings, implement a pong handler:
// Most browser WebSocket implementations handle ping/pong automatically.
// For Node.js with the 'ws' library:
const WebSocket = require("ws");
const ws = new WebSocket("wss://relayer.xoxno.com/ws");

ws.on("ping", () => {
  ws.pong();
});

Topic limits

Each connection can hold up to 64 active topic subscriptions simultaneously. This limit covers all topic types combined: gasStats, networkStats, accounts, address/{bech32}, and tx-status/{hash}. When you reach the limit, the Relayer returns:
{
  "action": "subscribe",
  "status": "fail",
  "reason": "topic limit reached"
}
To free slots:
  • Unsubscribe from topics you no longer need.
  • Unsubscribe from tx-status/{hash} topics once the transaction confirms.

Rate limiting

The Relayer enforces a rate limit of 64 control messages per 5-second window. Control messages are subscribe and unsubscribe actions. The relay and broadcast actions use backpressure from the internal aggregator instead of this rate limit. If you exceed the rate limit:
{
  "action": "subscribe",
  "status": "fail",
  "reason": "rate limit exceeded"
}
Exceeding 64 control messages within a 5-second window closes the connection. Batch your subscriptions at connection open.

Staying within the limit

  • Subscribe to all topics in a single burst at connection open (up to 64 topics).
  • Avoid subscribe/unsubscribe churn during normal operation.
  • If you need to rotate address subscriptions, batch the unsubscribes and subscribes together.

Message size

The maximum WebSocket message size is 4 MB. The Relayer rejects messages exceeding this limit and closes the connection. This limit applies to both inbound (client-to-server) and outbound (server-to-client) frames. In practice, you approach this limit only when sending large batch payloads.

Reconnection reconnect handling

WebSocket connections drop over time. Network blips, server deployments, and load balancer timeouts are normal. Design your client for automatic reconnection.

Exponential backoff

Start at 1 second and double on each consecutive failure, capping at 30 seconds. Reset the delay on successful connection.
class RelayerClient {
  constructor() {
    this.topics = new Set();
    this.retryDelay = 1000;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket("wss://relayer.xoxno.com/ws");

    this.ws.onopen = () => {
      this.retryDelay = 1000; // Reset on success

      // Re-subscribe to all tracked topics
      for (const topic of this.topics) {
        this.ws.send(JSON.stringify({
          action: "subscribe",
          topic
        }));
      }
    };

    this.ws.onclose = () => {
      console.log(`Reconnecting in ${this.retryDelay}ms`);
      setTimeout(() => this.connect(), this.retryDelay);
      this.retryDelay = Math.min(this.retryDelay * 2, 30000);
    };

    this.ws.onerror = () => this.ws.close();
  }

  subscribe(topic) {
    this.topics.add(topic);
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ action: "subscribe", topic }));
    }
  }

  unsubscribe(topic) {
    this.topics.delete(topic);
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ action: "unsubscribe", topic }));
    }
  }
}

Key principles

  • Re-subscribe after every reconnect. The server discards your subscriptions when the connection drops.
  • Track subscriptions client-side. Maintain a set of active topics so you can re-subscribe in a burst on reconnect.
  • Cap the backoff at 30 seconds. Waiting minutes between attempts delays recovery.
  • Handle errors gracefully. Treat onerror as a close trigger, not a fatal event.

REST endpoint limits

REST endpoints (/v1/sign, /v1/broadcast, /v1/network-stats/history) are rate-limited independently of WebSocket connections. The Relayer applies per-IP rate limiting with a sliding window. If you receive a 429 Too Many Requests response, back off and retry.
EndpointMethodNotes
/v1/signPOSTBatch co-signing (Relayer V3). Body: { transactions: [...] }
/v1/broadcastPOSTBatch broadcast. Body: { transactions: [...] }
/v1/network-stats/historyGETHistorical network statistics
/healthGETHealth check (no rate limit)