inicio

API

WebSocket

Datos de Seeryx en vivo por un solo WebSocket: suscríbete a una ronda para ambos libros, operaciones y volumen, y a tu cuenta; formatos y reconexiones.

Conectarse

URL
wss://seeryx.com/ws
Suscripciones
Una ronda y una cuenta por conexión. Una suscripción nueva del mismo tipo reemplaza la anterior; para seguir más rondas, usa más conexiones.
Entrega
El primer mensaje tras una suscripción es el estado completo. Después, solo llega un mensaje cuando su contenido cambia, en cuanto cambia.

Suscribirse

Envías
{ "subscribe": "round", "slug": "btc-updown-5m-1791916200" }
{ "subscribe": "account", "token": "eyJ0eXAiOiJKV1Qi..." }
Un slug de ronda o de mercado de eventos; la cuenta necesita tu token de inicio de sesión.

Mensajes

Recibes
{ "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: ambos libros completos (como GET /api/orderbook), las últimas 100 operaciones y el volumen.
  • account: exactamente GET /api/account. Cuando cambie heads, lee /api/activity o /api/settled para obtener las filas nuevas.
  • error deja el socket abierto; puede seguir otra suscripción.

Latidos

El servidor envía un ping de WebSocket cada 30 segundos; las bibliotecas de WebSocket responden con un pong por sí solas. Se cierra una conexión de la que no llega nada durante 120 segundos. Envía la primera suscripción en los 10 segundos siguientes a la conexión.

Reconexión

  • Reconéctate con una espera creciente (1, 2, 4… hasta 30 segundos) y vuelve a suscribirte: el primer mensaje trae el estado completo, así que no te pierdes nada.
  • Un cierre con el motivo "Token expired" significa que debes volver a iniciar sesión antes de suscribirte a la cuenta.
  • Cuando termine una ronda, suscríbete a la siguiente en la misma conexión.

Límites y códigos de cierre

LímiteValorSi se supera
Tamaño del mensaje4 KBSe cierra la conexión
Mensajes de una conexión60 por minutoCierre 1008 "Rate limit"
Mensajes de suscripción600 por minuto por dirección IP, 60 por cuenta{ "type": "error", "error": "Too many subscriptions" }
Conexiones abiertas50 por dirección IPHTTP 429 antes de la actualización a WebSocket, con Retry-After
Primera suscripciónEn un máximo de 10 segundosCierre 1008 "Handshake timeout"
Silencio120 segundos sin ninguna tramaCierre 1008 "Idle timeout"
Mensajes sin leer256 en colaCierre 1013 "Slow consumer"
Token de inicio de sesiónHasta que vence o se revocaCierre 1008 "Token expired" o "Token revoked"

Código

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(); },
  };
}
Un cliente que se reconecta por sí solo.
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);
  },
});