Hydra
API Reference

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

  1. WebSocket upgrade completes
  2. Server sends connected message
  3. Server sends initial snapshot (aircraft, vessels, signals, alerts, markets)
  4. 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])
	}
}