Connection URLs, protocol requirements, rate limits, and reconnection strategy for the XOXNO Relayer.
Connection URLs
| Endpoint | Protocol | Purpose |
|---|
wss://relayer.xoxno.com/ws | WebSocket (TLS) | Full-duplex: subscribe, relay, broadcast |
wss://relayer.xoxno.com/ws/stats | WebSocket (TLS) | Read-only network stats stream (no subscribe needed) |
https://relayer.xoxno.com | HTTPS | REST API base URL |
All WebSocket messages are JSON. The Relayer accepts binary frames for proto broadcast passthrough.
Limits at a glance
| Parameter | Value |
|---|
| Max topics per connection | 64 |
| Rate limit (control messages) | 64 per 5-second window |
| Max message size | 4 MB |
| Ping/pong heartbeat interval | 30 seconds |
| Max subscribers per address topic | 100 |
| Max unique address entries | 10,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.
| Endpoint | Method | Notes |
|---|
/v1/sign | POST | Batch co-signing (Relayer V3). Body: { transactions: [...] } |
/v1/broadcast | POST | Batch broadcast. Body: { transactions: [...] } |
/v1/network-stats/history | GET | Historical network statistics |
/health | GET | Health check (no rate limit) |