Real-time WebSocket streaming endpoints on the Hydra Node.
WebSocket Streaming
The Hydra Node serves real-time data via WebSocket. Two endpoints are available — one public and one API-key-gated with scope filtering.
GET /stream/live
Authentication: None (public, rate-limited by IP)
Open a WebSocket connection to receive all data in real-time.
const ws = new WebSocket('ws://localhost:4001/stream/live');
Connection Flow
- WebSocket upgrade completes
- Server sends
connectedmessage - Server sends initial snapshot (aircraft, vessels, signals, alerts, markets)
- Live updates stream continuously
Initial Snapshot
{ "type": "connected", "data": { "ts": 1711008000000 } }
{ "type": "aircraft", "data": { "positions": [...], "count": 42 } }
{ "type": "vessel", "data": { "positions": [...], "count": 18 } }
{ "type": "signal", "data": { "signals": [...] } }
{ "type": "alert", "data": { "alerts": [...] } }
{ "type": "market", "data": { "markets": [...] } }
{ "type": "online_count", "data": { "count": 3 } }
Live Update Messages
After the snapshot, incremental updates arrive as they're collected:
{ "type": "aircraft", "data": { "positions": [...], "count": 5 } }
{ "type": "signal", "data": { "signals": [{ "title": "...", ... }] } }
{ "type": "alert", "data": { "alerts": [{ "title": "...", ... }] } }
Keep-Alive
- Ping interval: 15 seconds
- Pong wait: 30 seconds
- Send buffer: 256 messages per client
If the client doesn't respond to pings within 30 seconds, the connection is closed.
Rate Limits
Maximum 10 concurrent connections per IP address.
GET /v1/stream
Authentication: API key required via x-api-key header or ?apiKey= query parameter.
Identical to /stream/live but with scope-based filtering. Only events matching the API key's scopes are delivered.
const ws = new WebSocket('ws://localhost:4001/v1/stream?apiKey=hyd_a1b2c3d4...');
Or with header (requires a WebSocket library that supports custom headers):
const ws = new WebSocket('ws://localhost:4001/v1/stream', {
headers: { 'x-api-key': 'hyd_a1b2c3d4...' }
});
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
apiKey |
string | — | API key for authentication |
snapshot |
string | "true" |
Set to "false" to skip initial snapshot |
Connection Confirmation
The connected message includes the resolved scopes:
{
"type": "connected",
"data": {
"scopes": ["signals", "aircraft", "markets"],
"ts": 1711008000000
}
}
Scope Filtering
Events are filtered based on the API key's scopes:
| Scope | Event Types Received |
|---|---|
signals |
signal (general signals) |
markets |
market, market_correlation |
aircraft |
aircraft |
vessels |
vessel |
alerts |
alert (MISSILE_ALERT only) |
airspace |
signal (AIRSPACE category) |
cyber |
signal (CYBER category) |
earthquakes |
signal (earthquake signals) |
social |
x_post, tg_message |
Skipping the Snapshot
For bots and pipelines that only care about new events, skip the initial data dump:
const ws = new WebSocket('ws://localhost:4001/v1/stream?apiKey=hyd_...&snapshot=false');
Authentication Errors
If the API key is invalid or missing, the server sends an error and closes:
{
"type": "error",
"data": {
"message": "Unauthorized — provide a valid API key via ?apiKey= or x-api-key header"
}
}
Message Format Reference
All messages follow the same structure:
{
"type": "<event_type>",
"data": { ... }
}
See the Hydra WebSocket Stream Event Types for the full payload reference — the node uses the same event format as the central Hydra stream.
Code Example
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/gorilla/websocket"
)
func main() {
c, _, err := websocket.DefaultDialer.Dial("ws://localhost:4001/stream/live", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
for {
_, raw, err := c.ReadMessage()
if err != nil {
log.Fatal(err)
}
var msg struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
json.Unmarshal(raw, &msg)
fmt.Printf("[%s] %s\n", msg.Type, string(msg.Data)[:80])
}
}