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..." }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 (comoGET /api/orderbook), las últimas 100 operaciones y el volumen.account: exactamenteGET /api/account. Cuando cambieheads, lee/api/activityo/api/settledpara obtener las filas nuevas.errordeja 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ímite | Valor | Si se supera |
|---|---|---|
| Tamaño del mensaje | 4 KB | Se cierra la conexión |
| Mensajes de una conexión | 60 por minuto | Cierre 1008 "Rate limit" |
| Mensajes de suscripción | 600 por minuto por dirección IP, 60 por cuenta | { "type": "error", "error": "Too many subscriptions" } |
| Conexiones abiertas | 50 por dirección IP | HTTP 429 antes de la actualización a WebSocket, con Retry-After |
| Primera suscripción | En un máximo de 10 segundos | Cierre 1008 "Handshake timeout" |
| Silencio | 120 segundos sin ninguna trama | Cierre 1008 "Idle timeout" |
| Mensajes sin leer | 256 en cola | Cierre 1013 "Slow consumer" |
| Token de inicio de sesión | Hasta que vence o se revoca | Cierre 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(); },
};
}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);
},
});