Streaming API
Real-time streaming endpoints for live updates.
Overview
Data Stream provides three streaming protocols:
| Protocol | Endpoint Pattern | Use Case |
|---|---|---|
| gRPC | grpc-datastream.hypetech.games:50051 | Type-safe, built-in filters |
| WebSocket | wss://host/ws/:game | Bidirectional, low latency |
| SSE | GET /sse/:game | Server push, auto-reconnect |
Authentication
All streaming endpoints require authentication. See the Authentication Guide for details.
| Protocol | Auth Method |
|---|---|
| gRPC | API key via x-api-key metadata |
| WebSocket | Token via Sec-WebSocket-Protocol header |
| SSE | API key via X-API-Key header or ?api_key= query parameter |
WebSocket
Endpoints
ws://localhost:3000/ws/:game
ws://localhost:3000/ws/type/:type
wss://datastream.hypetech.games/ws/:game
wss://datastream.hypetech.games/ws/type/:type
Connection
WebSocket requires a short-lived token obtained via REST API:
// Step 1: Get token using API key
const tokenResponse = await fetch('/api/auth/token', {
method: 'POST',
headers: { 'X-API-Key': 'sk_live_xxxxx' }
});
const { token } = await tokenResponse.json();
// Step 2: Connect with token via Sec-WebSocket-Protocol
const ws = new WebSocket('wss://datastream.hypetech.games/ws/crash', [token]);
// By type
const wsType = new WebSocket('wss://datastream.hypetech.games/ws/type/multiplier', [token]);
Message Format
Messages are JSON-encoded round objects:
{
"round_id": 512,
"game_id": 1,
"game_slug": "crash",
"game_type": "multiplier",
"finished_at": "2026-01-17T00:45:42Z",
"extras": "{\"point\": \"11.72\"}",
"timestamp": 1768621542123
}
Events
| Event | Description |
|---|---|
onopen | Connection established |
onmessage | New round received |
onclose | Connection closed |
onerror | Error occurred |
Example
// Get token first
async function connectWithAuth() {
const tokenRes = await fetch('/api/auth/token', {
method: 'POST',
headers: { 'X-API-Key': 'sk_live_xxxxx' }
});
const { token } = await tokenRes.json();
const ws = new WebSocket('wss://datastream.hypetech.games/ws/crash', [token]);
ws.onopen = () => {
console.log('Connected');
};
ws.onmessage = (event) => {
const round = JSON.parse(event.data);
console.log(`Round ${round.round_id}: ${round.extras}`);
};
ws.onclose = (event) => {
console.log(`Closed: ${event.code}`);
// Implement reconnection with new token
};
ws.onerror = (error) => {
console.error('Error:', error);
};
}
Server-Sent Events (SSE)
Endpoints
GET /sse/:game
GET /sse/type/:type
Headers
Request:
Accept: text/event-stream
Response:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Event Types
| Event | Description |
|---|---|
initial | First message with latest result |
message | New round result |
| (comment) | Heartbeat (: prefix) |
Event Format
event: initial
data: {"round_id":512,"game_slug":"crash",...}
: heartbeat 1705432800
event: message
data: {"round_id":513,"game_slug":"crash",...}
Example
SSE supports both header and query parameter for authentication. Use query parameter for browser's native EventSource (which doesn't support headers):
// Browser: Use query parameter (EventSource doesn't support headers)
const apiKey = 'sk_live_xxxxx';
const sse = new EventSource(`/sse/crash?api_key=${encodeURIComponent(apiKey)}`);
// Initial data
sse.addEventListener('initial', (e) => {
const round = JSON.parse(e.data);
console.log('Initial:', round);
});
// New messages
sse.addEventListener('message', (e) => {
const round = JSON.parse(e.data);
console.log('New round:', round);
});
// Connection events
sse.onopen = () => console.log('Connected');
sse.onerror = () => console.log('Reconnecting...');
curl Testing
# With header (recommended)
curl -N -H "X-API-Key: sk_live_xxxxx" "https://datastream.hypetech.games/sse/crash"
# Or with query parameter
curl -N "https://datastream.hypetech.games/sse/crash?api_key=sk_live_xxxxx"
Message Schema
All streaming protocols use the same message format:
interface Round {
round_id: number; // Unique identifier
game_id: number; // Database ID
game_slug: string; // URL-friendly name
game_type: string; // Category
finished_at: string; // ISO 8601 timestamp
extras: string; // JSON-encoded game data
timestamp: number; // Unix milliseconds
}
Extras Schema
Crash
{"point": "11.72"}
Double
{"color": "red", "number": 5}
Slot
{"reels": ["cherry", "lemon", "orange"], "multiplier": "2.00"}
Wall Street
{"trending": "up"}
Subscription Patterns
By Game
Subscribe to a specific game:
/ws/crash → Only crash results
/sse/double → Only double results
By Type
Subscribe to all games of a type:
/ws/type/multiplier → crash + slot results
/sse/type/color → double results
Latency Characteristics
| Protocol | Typical Latency | Notes |
|---|---|---|
| gRPC | ~2ms | HTTP/2 streaming |
| WebSocket | ~5ms | TCP overhead |
| SSE | ~50ms | HTTP chunked |
Latency Measurement
Each message includes a timestamp field (Unix ms). Calculate latency:
ws.onmessage = (event) => {
const round = JSON.parse(event.data);
const latency = Date.now() - round.timestamp;
console.log(`Latency: ${latency}ms`);
};
Connection Management
WebSocket Reconnection
class ReconnectingWebSocket {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectDelay = 1000;
this.maxDelay = 30000;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.reconnectDelay = 1000;
};
this.ws.onclose = () => {
this.scheduleReconnect();
};
}
scheduleReconnect() {
setTimeout(() => {
this.connect();
}, this.reconnectDelay);
// Exponential backoff
this.reconnectDelay = Math.min(
this.reconnectDelay * 2,
this.maxDelay
);
}
}
SSE (Auto-Reconnect)
SSE reconnects automatically. Just handle the error event:
sse.onerror = () => {
console.log('Connection lost, auto-reconnecting...');
// Browser handles reconnection
};
Error Handling
WebSocket Errors
| Code | Meaning | Action |
|---|---|---|
| 1000 | Normal close | Clean shutdown |
| 1001 | Going away | Server restart |
| 1006 | Abnormal | Network issue |
| 1011 | Server error | Retry with backoff |
SSE Errors
SSE auto-reconnects on network errors. Handle via:
sse.onerror = (event) => {
if (event.target.readyState === EventSource.CLOSED) {
console.log('Connection closed');
} else {
console.log('Reconnecting...');
}
};
Best Practices
1. Choose the Right Protocol
// gRPC for backend services with type safety
if (needsTypeSafety && !isBrowser) {
return new GrpcClient('grpc-datastream.hypetech.games:50051');
}
// WebSocket for bidirectional
if (needsBidirectional) {
return new WebSocket('wss://host/ws/crash');
}
// SSE for simple server push
if (needsOnlyServerPush) {
return new EventSource('/sse/crash?api_key=...');
}
2. Handle Reconnection
Always implement reconnection for WebSocket:
ws.onclose = () => {
setTimeout(() => connect(), 3000);
};
3. Parse Extras Safely
function parseExtras(extras) {
try {
return JSON.parse(extras);
} catch (e) {
console.error('Invalid extras:', extras);
return {};
}
}
4. Monitor Latency
function handleMessage(round) {
const latency = Date.now() - round.timestamp;
if (latency > 100) {
console.warn(`High latency: ${latency}ms`);
}
}
5. Graceful Degradation
async function connect() {
// Try WebSocket first for lower latency
try {
return await connectWebSocket();
} catch (e) {}
// Fallback to SSE (works everywhere)
return connectSSE();
}