Hydra
API Reference

Complete code examples for connecting to the Hydra WebSocket stream.

Code Examples

All examples require a valid API key. See API Keys to generate one.

JavaScript / Node.js

Basic connection with message handling

const API_KEY = 'hydra_k1_abc123...';
const ws = new WebSocket(`wss://api.hydra.fast/v1/stream?apiKey=${API_KEY}`);

ws.onopen = () => console.log('Connected to Hydra stream');

ws.onmessage = (event) => {
  const { type, data } = JSON.parse(event.data);

  switch (type) {
    case 'connected':
      console.log('Stream ready, scopes:', data.scopes);
      break;

    case 'aircraft':
      console.log(`${data.count} aircraft positions`);
      data.positions.forEach(ac => {
        console.log(`  ${ac.callsign || ac.icaoHex} [${ac.category}] ${ac.lat}, ${ac.lng}`);
      });
      break;

    case 'signal':
      data.signals.forEach(sig => {
        console.log(`[${sig.severity}] ${sig.title}`);
      });
      break;

    case 'alert':
      data.alerts.forEach(alert => {
        console.log(`ALERT: ${alert.title}`);
      });
      break;

    case 'vessel':
      console.log(`${data.count} vessel positions`);
      break;

    case 'market':
      data.markets.forEach(m => {
        if (Math.abs(m.priceChange) > 0.02) {
          console.log(`Market move: ${m.question} (${(m.priceChange * 100).toFixed(1)}pp)`);
        }
      });
      break;
  }
};

ws.onclose = () => console.log('Disconnected');
ws.onerror = (err) => console.error('WebSocket error:', err);

Robust client with auto-reconnect

class HydraStream {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.attempt = 0;
    this.ws = null;
    this.handlers = new Map();
  }

  on(type, handler) {
    this.handlers.set(type, handler);
    return this;
  }

  connect() {
    this.ws = new WebSocket(`wss://api.hydra.fast/v1/stream?apiKey=${this.apiKey}`);

    this.ws.onopen = () => {
      console.log('Connected');
      this.attempt = 0;
    };

    this.ws.onmessage = (event) => {
      const { type, data } = JSON.parse(event.data);
      const handler = this.handlers.get(type);
      if (handler) handler(data);
    };

    this.ws.onclose = () => {
      const delay = Math.min(1000 * Math.pow(2, this.attempt), 30000);
      console.log(`Reconnecting in ${delay}ms...`);
      setTimeout(() => {
        this.attempt++;
        this.connect();
      }, delay);
    };
  }

  disconnect() {
    if (this.ws) this.ws.close();
  }
}

// Usage
const stream = new HydraStream('hydra_k1_abc123...');

stream
  .on('aircraft', (data) => {
    console.log(`Tracking ${data.count} aircraft`);
  })
  .on('signal', (data) => {
    data.signals.forEach(s => console.log(`[${s.severity}] ${s.title}`));
  })
  .on('alert', (data) => {
    data.alerts.forEach(a => console.log(`ALERT: ${a.title}`));
  })
  .connect();

Python

Using websockets library

import asyncio
import json
import websockets

API_KEY = "hydra_k1_abc123..."

async def connect():
    uri = f"wss://api.hydra.fast/v1/stream?apiKey={API_KEY}"
    attempt = 0

    while True:
        try:
            async with websockets.connect(uri) as ws:
                print("Connected to Hydra stream")
                attempt = 0

                async for raw in ws:
                    message = json.loads(raw)
                    msg_type = message["type"]
                    data = message["data"]

                    if msg_type == "connected":
                        print(f"Stream ready, scopes: {data['scopes']}")

                    elif msg_type == "aircraft":
                        print(f"Aircraft: {data['count']} positions")
                        for ac in data["positions"]:
                            cs = ac.get("callsign") or ac["icaoHex"]
                            print(f"  {cs} [{ac['category']}] {ac['lat']}, {ac['lng']}")

                    elif msg_type == "signal":
                        for sig in data["signals"]:
                            print(f"[{sig['severity']}] {sig['title']}")

                    elif msg_type == "alert":
                        for alert in data["alerts"]:
                            print(f"ALERT: {alert['title']}")

                    elif msg_type == "vessel":
                        print(f"Vessels: {data['count']} positions")

                    elif msg_type == "market":
                        for m in data["markets"]:
                            if abs(m.get("priceChange", 0)) > 0.02:
                                pct = m["priceChange"] * 100
                                print(f"Market: {m['question']} ({pct:+.1f}pp)")

        except (websockets.ConnectionClosed, ConnectionError) as e:
            delay = min(2 ** attempt, 30)
            print(f"Disconnected: {e}. Reconnecting in {delay}s...")
            await asyncio.sleep(delay)
            attempt += 1

asyncio.run(connect())

Alert monitor (minimal)

import asyncio
import json
import websockets

API_KEY = "hydra_k1_abc123..."

async def monitor_alerts():
    uri = f"wss://api.hydra.fast/v1/stream?apiKey={API_KEY}"
    async with websockets.connect(uri) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg["type"] == "alert":
                for alert in msg["data"]["alerts"]:
                    print(f"[{alert['severity']}] {alert['title']}")
                    print(f"  Region: {alert.get('region', 'N/A')}")
                    print(f"  Time:   {alert['createdAt']}")

asyncio.run(monitor_alerts())

Go

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

const apiKey = "hydra_k1_abc123..."

type Message struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"`
}

type AircraftData struct {
	Count     int `json:"count"`
	Positions []struct {
		IcaoHex  string  `json:"icaoHex"`
		Callsign *string `json:"callsign"`
		Lat      float64 `json:"lat"`
		Lng      float64 `json:"lng"`
		Category string  `json:"category"`
	} `json:"positions"`
}

func main() {
	for attempt := 0; ; attempt++ {
		err := stream()
		if err != nil {
			delay := min(time.Duration(1<<attempt)*time.Second, 30*time.Second)
			log.Printf("Disconnected: %v. Reconnecting in %s...", err, delay)
			time.Sleep(delay)
		}
	}
}

func stream() error {
	url := "wss://api.hydra.fast/v1/stream?apiKey=" + apiKey
	headers := http.Header{"x-api-key": {apiKey}}

	c, _, err := websocket.DefaultDialer.Dial(url, headers)
	if err != nil {
		return err
	}
	defer c.Close()
	log.Println("Connected")

	for {
		_, raw, err := c.ReadMessage()
		if err != nil {
			return err
		}

		var msg Message
		json.Unmarshal(raw, &msg)

		switch msg.Type {
		case "aircraft":
			var data AircraftData
			json.Unmarshal(msg.Data, &data)
			fmt.Printf("Aircraft: %d positions\n", data.Count)
		case "signal":
			fmt.Printf("Signal update received\n")
		case "alert":
			fmt.Printf("ALERT received\n")
		}
	}
}

CLI (websocat)

WebSocket connections aren't natively supported by cURL, but you can use websocat for testing:

# Install: cargo install websocat
websocat "wss://api.hydra.fast/v1/stream?apiKey=hydra_k1_abc123..." | jq .

Filter to specific event types:

websocat "wss://api.hydra.fast/v1/stream?apiKey=hydra_k1_abc123..." | \
  jq 'select(.type == "alert" or .type == "signal")'