WebSocket API
Real-time round updates via WebSocket connection.
Connection
wss://rng.dev/ws/rounds
Compression: The server supports permessage-deflate compression (60-80% bandwidth reduction). Most WebSocket clients negotiate this automatically. Latency impact is negligible (<1ms).
On Connect: The server immediately sends the current round when you connect. You don't need to wait up to 60 seconds for the next round.
Quick Start
JavaScript
const ws = new WebSocket('wss://rng.dev/ws/rounds');
ws.onopen = () => {
console.log('Connected to beacon');
};
ws.onmessage = (event) => {
const round = JSON.parse(event.data);
console.log(`Round ${round.round}: die = ${round.die_value}`);
};
ws.onclose = () => {
console.log('Disconnected');
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
Python
import asyncio
import websockets
import json
async def listen():
uri = "wss://rng.dev/ws/rounds"
async with websockets.connect(uri) as websocket:
async for message in websocket:
round_data = json.loads(message)
print(f"Round {round_data['round']}: die = {round_data['die_value']}")
asyncio.run(listen())
Message Format
Round Update
Sent every 60 seconds when a new round is generated:
{
"type": "round",
"round": 12345678,
"output_hash": "a3f2e8c9d1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0",
"die_value": 4,
"generated_at": "2026-03-09T15:00:00Z",
"status": "complete",
"sources_available": 6,
"sources_total": 6,
"inputs": {
"algorand": "34567890:QWERTY...XYZ",
"avalanche": "12345678:0xabc123...",
"bitcoin": "00000000000000000002a7c4...3f:831245",
"cardano": "8765432:def456...",
"ethereum": "0x8a3f2e...b9:19234567",
"solana": "245678901:5eykt4Uy...Gno"
}
}
The inputs field contains the exact strings used to generate output_hash, enabling real-time verification. See Verification Guide for details.
Heartbeat
Sent every 30 seconds to keep connection alive:
{
"type": "heartbeat",
"timestamp": "2026-03-09T15:00:30Z"
}
Error
Sent when an error occurs:
{
"type": "error",
"code": "GENERATION_FAILED",
"message": "Failed to generate round: insufficient sources"
}
Connection Management
Automatic Reconnection
Implement exponential backoff for reconnection:
class BeaconClient {
constructor() {
this.reconnectDelay = 1000;
this.maxReconnectDelay = 30000;
this.connect();
}
connect() {
this.ws = new WebSocket('wss://rng.dev/ws/rounds');
this.ws.onopen = () => {
console.log('Connected');
this.reconnectDelay = 1000; // Reset on successful connection
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'round') {
this.onRound(data);
}
};
this.ws.onclose = () => {
console.log(`Reconnecting in ${this.reconnectDelay}ms...`);
setTimeout(() => this.connect(), this.reconnectDelay);
this.reconnectDelay = Math.min(
this.reconnectDelay * 2,
this.maxReconnectDelay
);
};
}
onRound(round) {
// Override this method
console.log('New round:', round);
}
}
Heartbeat Handling
If no heartbeat received in 60 seconds, consider the connection stale:
let lastHeartbeat = Date.now();
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'heartbeat') {
lastHeartbeat = Date.now();
}
};
setInterval(() => {
if (Date.now() - lastHeartbeat > 60000) {
console.log('Connection stale, reconnecting...');
ws.close();
}
}, 10000);
Rate Limits
| Limit | Value |
|---|---|
| Connections per IP | 10 |
| Messages per minute | N/A (server-initiated only) |
Exceeding connection limits will result in connection rejection.
Best Practices
Do
- Implement automatic reconnection with backoff
- Handle heartbeat messages to detect stale connections
- Parse and validate incoming JSON
- Close connections cleanly when done
Don't
- Open multiple connections from the same client
- Ignore connection errors
- Assume messages arrive in order (they will, but verify)
- Keep connections open when not needed
Example: React Hook
import { useState, useEffect, useCallback } from 'react';
interface Round {
round: number;
output_hash: string;
die_value: number;
generated_at: string;
status: string;
sources_available: number;
sources_total: number;
inputs: Record<string, string>;
}
export function useBeaconRounds() {
const [currentRound, setCurrentRound] = useState<Round | null>(null);
const [connected, setConnected] = useState(false);
useEffect(() => {
let ws: WebSocket;
let reconnectTimeout: NodeJS.Timeout;
const connect = () => {
ws = new WebSocket('wss://rng.dev/ws/rounds');
ws.onopen = () => setConnected(true);
ws.onclose = () => {
setConnected(false);
reconnectTimeout = setTimeout(connect, 3000);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'round') {
setCurrentRound(data);
}
};
};
connect();
return () => {
clearTimeout(reconnectTimeout);
ws?.close();
};
}, []);
return { currentRound, connected };
}
Debugging
Test Connection
# Using websocat
websocat wss://rng.dev/ws/rounds
# Using wscat
npx wscat -c wss://rng.dev/ws/rounds
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Connection refused (1008) | Too many connections from your IP (max 10) | Close unused connections |
| No messages after connect | Should receive current round immediately | Check connection is open |
| Parse error | Invalid JSON | Report bug to support |
Next Steps
- REST API - HTTP endpoints
- Quick Start - Getting started guide