Transport Protocols
Relay supports multiple transport protocols beyond WebSockets. Choose the right one for your use case.
WebSockets (Default)
Full-duplex bidirectional communication. Best for interactive real-time apps.
- Bidirectional: client and server can send messages anytime
- Lowest latency
- Persistent connection
- Uses
pusher-jsor any Pusher-compatible client
const pusher = new Pusher('your-key', {
wsHost: 'relay.example.com',
wsPort: 6001,
cluster: 'mt1',
});
Best for: Chat, collaboration, gaming, live dashboards
Server-Sent Events (SSE)
Server-to-client only streaming over HTTP. Simpler than WebSockets, with automatic reconnection built into the browser.
- One-way: server pushes events to the client
- Automatic reconnection with
Last-Event-ID - Works through proxies and firewalls that block WebSockets
- Native browser support via
EventSource
Connecting
const source = new EventSource(
'/api/v1/apps/YOUR_APP_ID/sse?channels=notifications,alerts',
{ headers: { 'Authorization': 'Bearer rly_your_token' } }
);
source.addEventListener('connected', (e) => {
console.log('Connected:', JSON.parse(e.data));
});
source.addEventListener('new-message', (e) => {
const data = JSON.parse(e.data);
console.log('Channel:', data.channel, 'Data:', data.data);
});
source.onerror = () => {
console.log('Connection lost — reconnecting automatically...');
};
Query Parameters
| Param | Description |
|---|---|
channels |
Comma-separated channel names (required) |
last_event_id |
Resume from this event ID |
Headers
The server sends Last-Event-ID with each event. On reconnection, the browser automatically sends this header so you don't miss events.
Best for: Live feeds, notifications, log streaming, AI token streaming, stock tickers
Long Polling
Client sends a request; server holds it open until events arrive or a timeout expires. Simulates real-time with standard HTTP.
- Works everywhere (no WebSocket or SSE support needed)
- Higher latency than WebSockets/SSE
- Each "connection" is a series of HTTP requests
Usage
async function longPoll(lastEventId = null) {
const params = new URLSearchParams({
channels: 'my-channel',
timeout: 25,
});
if (lastEventId) params.set('last_event_id', lastEventId);
const res = await fetch(
`/api/v1/apps/YOUR_APP_ID/poll?${params}`,
{ headers: { 'Authorization': 'Bearer rly_your_token' } }
);
const { events, last_event_id } = await res.json();
for (const event of events) {
console.log('Received:', event.channel, event.event, event.data);
}
// Immediately reconnect for next batch
longPoll(last_event_id);
}
longPoll();
Query Parameters
| Param | Description |
|---|---|
channels |
Comma-separated channel names (required) |
since |
ISO 8601 timestamp to fetch events from |
last_event_id |
Resume from this event ID |
timeout |
Max hold time in seconds (default 25, max 30) |
Best for: Environments where WebSockets and SSE are blocked, legacy browser support
HTTP Polling
Simplest option. Client polls on a regular interval.
Usage
setInterval(async () => {
const res = await fetch(
`/api/v1/apps/YOUR_APP_ID/events/poll?channels=my-channel&since=${lastCheck}`,
{ headers: { 'Authorization': 'Bearer rly_your_token' } }
);
const { events, timestamp } = await res.json();
lastCheck = timestamp;
for (const event of events) {
console.log(event);
}
}, 3000); // Poll every 3 seconds
Query Parameters
| Param | Description |
|---|---|
channels |
Comma-separated channel names (required) |
since |
ISO 8601 timestamp |
last_event_id |
Resume from this event ID |
limit |
Max events per response (default 50, max 200) |
The response includes a next_poll_url for convenience.
Best for: Infrequent updates, dashboards that refresh every few seconds, simple integrations
Choosing a Transport
| Transport | Direction | Latency | Complexity | Browser Support |
|---|---|---|---|---|
| WebSocket | Bidirectional | ~1ms | Medium | Modern browsers |
| SSE | Server → Client | ~50ms | Low | All browsers |
| Long Poll | Server → Client | ~100ms | Low | Everything |
| HTTP Poll | Server → Client | Interval-based | Minimal | Everything |
Decision Guide
- Need bidirectional? → WebSocket
- Server-to-client only, need low latency? → SSE
- WebSocket/SSE blocked? → Long Polling
- Updates every few seconds is fine? → HTTP Polling
- AI streaming / token-by-token? → SSE
All transports use the same authentication (Bearer token, Key+Secret headers, or HMAC).