Startseite

API

WebSocket

Live-Daten von Seeryx über einen WebSocket: Abonniere eine Runde für beide Orderbücher, Trades und Volumen sowie dein Konto; Nachrichtenformate, Neuverbindung.

Verbinden

URL
wss://seeryx.com/ws
Abonnements
Eine Runde und ein Konto pro Verbindung. Ein neues Abonnement derselben Art ersetzt das alte; für mehr Runden brauchst du mehr Verbindungen.
Zustellung
Die erste Nachricht nach einem Abonnement ist der vollständige Zustand. Danach kommt eine Nachricht nur, wenn sich ihr Inhalt geändert hat, und zwar sofort.

Abonnieren

Du sendest
{ "subscribe": "round", "slug": "btc-updown-5m-1791916200" }
{ "subscribe": "account", "token": "eyJ0eXAiOiJKV1Qi..." }
Ein Runden-Slug oder der Slug eines Ereignismarkts; das Konto braucht dein Anmelde-Token.

Nachrichten

Du erhältst
{ "type": "round", "slug": "btc-updown-5m-1791916200",
  "up":   { "bids": [[13, 10], [12, 100]], "asks": [[16, 100], [17, 100]] },
  "down": { "bids": [[84, 100], [83, 100]], "asks": [[87, 10], [88, 100]] },
  "trades": [{ "id": 13, "side": "down", "action": "buy", "shares": 10, "price": 53, "time": 1791916330063 }, ...],
  "volume": { "volume": 55.845724, "trades": 13 } }

{ "type": "account",
  "positions": [{ "slug": "btc-updown-5m-1791916200", "side": "down", "shares": 50.3462, "cost": 18.126554 }],
  "orders": [{ "id": 25538, "slug": "btc-updown-5m-1791916200", "side": "down", "action": "buy", "shares": 10, "limit": 48, "createdAt": 1791916330045 }],
  "balance": { "available": 474.097965, "locked": 0, "total": 474.097965, "currency": "USDC" },
  "heads": { "activity": 116, "settled": null } }

{ "type": "error", "error": "Invalid subscription or authentication" }
  • round: beide Orderbücher vollständig (wie GET /api/orderbook), die letzten 100 Trades und das Volumen.
  • account: genau GET /api/account. Wenn sich heads ändert, lies /api/activity oder /api/settled für die neuen Zeilen.
  • error lässt den Socket offen; ein weiteres Abonnement kann folgen.

Ping und Pong

Der Server sendet alle 30 Sekunden einen WebSocket-Ping; WebSocket-Bibliotheken antworten von selbst mit einem Pong. Eine Verbindung, von der 120 Sekunden lang nichts ankommt, wird geschlossen. Sende das erste Abonnement innerhalb von 10 Sekunden nach dem Verbinden.

Neuverbindung

  • Verbinde dich mit wachsender Verzögerung neu (1, 2, 4 … bis zu 30 Sekunden) und abonniere erneut: Die erste Nachricht bringt den vollständigen Zustand, sodass nichts verloren geht.
  • Ein Close mit dem Grund „Token expired“ bedeutet: Melde dich erneut an, bevor du das Konto abonnierst.
  • Wenn eine Runde endet, abonniere die nächste über dieselbe Verbindung.

Limits und Close-Codes

LimitWertBei Überschreitung
Nachrichtengröße4 KBDie Verbindung wird geschlossen
Nachrichten einer Verbindung60 pro MinuteClose 1008 „Rate limit“
Abonnement-Nachrichten600 pro Minute und IP-Adresse, 60 pro Konto{ "type": "error", "error": "Too many subscriptions" }
Offene Verbindungen50 pro IP-AdresseHTTP 429 vor dem Upgrade, mit Retry-After
Erstes AbonnementInnerhalb von 10 SekundenClose 1008 „Handshake timeout“
Stille120 Sekunden ohne jeden FrameClose 1008 „Idle timeout“
Ungelesene Nachrichten256 in der WarteschlangeClose 1013 „Slow consumer“
Anmelde-TokenBis es abläuft oder widerrufen wirdClose 1008 „Token expired“ oder „Token revoked“

Code

live.ts
// live.ts: a WebSocket client that reconnects by itself. Node 22+ (WebSocket is built in) or any browser.
import { ACCESS, BASE } from './seeryx';

export type Message = { type: 'round' | 'account' | 'error' } & Record<string, unknown>;

export function live(options: { slug?: string; token?: string; onMessage: (message: Message) => void }) {
  let socket: WebSocket | null = null;
  let attempt = 0;
  let stopped = false;
  let { slug, token } = options;
  const gate = ACCESS ? { access: ACCESS } : {};
  const send = (message: object) => {
    if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ ...message, ...gate }));
  };

  function connect() {
    socket = new WebSocket(`${BASE.replace(/^http/, 'ws')}/ws`);
    socket.onopen = () => {
      attempt = 0;
      if (slug) send({ subscribe: 'round', slug });
      if (token) send({ subscribe: 'account', token });
    };
    socket.onmessage = (event) => options.onMessage(JSON.parse(String(event.data)));
    socket.onclose = (event) => {
      if (stopped) return;
      if (event.reason === 'Access code required') { stopped = true; return; } // get a new access token
      if (event.reason === 'Token expired') token = undefined;                   // sign in again for it
      setTimeout(connect, Math.min(30_000, 1000 * 2 ** attempt++));              // 1 s, 2 s, 4 s ... 30 s
    };
  }
  connect();

  return {
    // one round per connection: subscribing to the next round replaces the previous one
    follow(next: string) { slug = next; send({ subscribe: 'round', slug }); },
    close() { stopped = true; socket?.close(); },
  };
}
Ein Client, der sich selbst neu verbindet.
watch.ts
import { api, loadWallet, signIn } from './seeryx';
import { live } from './live';

const token = await signIn(await loadWallet());
const { slug } = await api<{ slug: string }>('/markets/BTC/5m/current');
live({
  slug, token, onMessage: (m) => {
    if (m.type === 'round') console.log('Up book', m.up);
    if (m.type === 'account') console.log('cash', m.balance);
  },
});