Connection lifecycle, authentication, heartbeat, and reconnection patterns for XOXNO WebSocket APIs.
Endpoints
The Relayer exposes two WebSocket endpoints:
Endpoint Mode Description wss://relayer.xoxno.com/wsFull-duplex Subscribe to topics, broadcast transactions, relay transactions wss://relayer.xoxno.com/ws/statsRead-only Pushes networkStats at adaptive intervals. No subscribe required.
The /ws/stats endpoint operates independently. Connect to both when your application needs subscription-based topics alongside the network stats stream.
Connection lifecycle
Client Server
| |
|──── WebSocket handshake ─────────>|
|<──── 101 Switching Protocols ─────|
| |
|──── subscribe { gasStats } ──────>|
|<──── { status: "ok" } ───────────|
| |
|<──── gasStats event ─────────────|
|<──── gasStats event ─────────────|
| |
|──── relay { tx } ────────────────>|
|<──── { hashes: [...] } ──────────|
| |
|──── unsubscribe { gasStats } ────>|
|<──── { status: "ok" } ───────────|
| |
|──── close ───────────────────────>|
|<──── close ──────────────────────|
Connect: Open a WebSocket connection to either endpoint.
Subscribe: Send subscribe actions to register for topics (/ws/stats subscribes automatically).
Receive events: The server pushes JSON events for each subscribed topic.
Send actions: Submit broadcast or relay actions to process transactions.
Unsubscribe: Remove subscriptions you no longer need.
Disconnect: Close the connection cleanly.
Authentication
Neither WebSocket endpoint requires authentication. The server treats all connections equally.
All messages use JSON encoding. Text frames are the standard transport. The server also accepts binary frames for legacy JSON payloads and protobuf batch broadcast.
Maximum message size: 4 MB
Encoding: UTF-8 JSON
Client-to-server messages include an action field. Server-to-client messages include either a type field (for events) or an action + status field (for action responses).
Heartbeat
The server sends a WebSocket ping frame every 30 seconds . Your client must respond with a pong frame. Most WebSocket libraries handle this automatically.
If the server receives no pong within the next ping interval, it closes the connection.
Connection limits
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
Sending more than 64 control messages within a 5-second window triggers a rate-limit error. Both subscribe and unsubscribe actions count toward this limit.
Reconnection strategy
Use exponential backoff with a cap:
Attempt Delay 1 1 second 2 2 seconds 3 4 seconds 4 8 seconds 5 16 seconds 6+ 30 seconds (cap)
After reconnecting, re-subscribe to all topics. The server does not persist subscriptions across connections.
Connection examples
JavaScript
Python
Rust
websocat
const ws = new WebSocket ( 'wss://relayer.xoxno.com/ws' );
ws . onopen = () => {
console . log ( 'Connected' );
ws . send ( JSON . stringify ({ action: 'subscribe' , topic: 'gasStats' }));
};
ws . onmessage = ( event ) => {
const msg = JSON . parse ( event . data );
console . log ( 'Received:' , msg . type || msg . action );
};
ws . onclose = () => {
console . log ( 'Disconnected: implement reconnection here' );
};
import asyncio
import json
import websockets
async def connect ():
uri = "wss://relayer.xoxno.com/ws"
async with websockets.connect(uri) as ws:
await ws.send(json.dumps({
"action" : "subscribe" ,
"topic" : "gasStats"
}))
async for message in ws:
msg = json.loads(message)
print ( f "Received: { msg.get( 'type' ) or msg.get( 'action' ) } " )
asyncio.run(connect())
use futures_util :: { SinkExt , StreamExt };
use serde_json :: json;
use tokio_tungstenite :: connect_async;
#[tokio :: main]
async fn main () {
let ( mut ws , _ ) = connect_async ( "wss://relayer.xoxno.com/ws" )
. await
. expect ( "failed to connect" );
let subscribe = json! ({
"action" : "subscribe" ,
"topic" : "gasStats"
});
ws . send ( subscribe . to_string () . into ()) . await . unwrap ();
while let Some ( Ok ( msg )) = ws . next () . await {
if let Ok ( text ) = msg . into_text () {
println! ( "Received: {text}" );
}
}
}
# Install: cargo install websocat
# Connect and subscribe:
echo '{"action":"subscribe","topic":"gasStats"}' | \
websocat wss://relayer.xoxno.com/ws
Read-only stats stream
The /ws/stats endpoint requires no subscribe action. Data arrives as soon as you connect:
const ws = new WebSocket ( 'wss://relayer.xoxno.com/ws/stats' );
ws . onmessage = ( event ) => {
const msg = JSON . parse ( event . data );
// msg.type === 'networkStats'
const { network , mempool } = msg . data ;
console . log ( `TPS: ${ network . currentTps } | Mempool: ${ mempool . estimatedQueue } ` );
};
Related pages
subscribe Subscribe to real-time topics: gas stats, network stats, account deltas, and tx status.
broadcast Submit pre-signed transactions for P2P broadcast via WebSocket.
gasStats Per-shard gas price and PPU statistics, every 50ms.
networkStats Full network snapshot: TPS, blocks, mempool, validators, health.