> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apinn.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Flujo WebSocket (suscripciones)

> Suscríbase a los deportes, ligas, eventos o mercados que realmente sigue, y reciba solo esos.

<Note>
  Los flujos en tiempo real requieren el plan **Sharp**.
</Note>

`wss://api.apinn.io/ws` envía los movimientos de cuotas igual que el [flujo SSE](/es/concepts/streaming), con una diferencia que importa: la conexión es **bidireccional**, así que puede decirle al servidor lo que quiere. Sin suscripción recibe todo el libro — exactamente lo que envía el flujo SSE.

Pase su clave en la cabecera `X-API-Key`, o como `?key=SU_CLAVE` en la URL (un navegador no puede añadir cabeceras a una conexión WebSocket).

```javascript Node.js theme={null}
import WebSocket from "ws";

const sock = new WebSocket("wss://api.apinn.io/ws?key=SU_CLAVE");

sock.on("open", () => {
  sock.send(JSON.stringify({ op: "subscribe", sports: [29], markets: ["moneyline"] }));
});

sock.on("message", (buf) => {
  const m = JSON.parse(buf);
  if (m.t === "snapshot") {
    // estado completo y actual de su suscripción
    console.log(m.count, "líneas");
  } else if (m.t === "delta") {
    // solo las líneas que cambiaron, dentro de su ámbito
    for (const row of m.rows) console.log(row.event_id, row.market, row.odds1, row.odds2);
  }
});
```

## Suscribirse

Envíe `{"op": "subscribe", ...}` con cualquier combinación de los filtros siguientes. Son acumulativos: una línea debe cumplir todos los filtros que defina.

| Filtro    | Tipo               | Ejemplo                                        |
| --------- | ------------------ | ---------------------------------------------- |
| `sports`  | ids de deporte     | `{"sports": [29, 33]}`                         |
| `leagues` | ids de liga        | `{"leagues": [2436]}`                          |
| `events`  | ids de evento      | `{"events": [1632682788]}`                     |
| `markets` | nombres de mercado | `{"markets": ["moneyline", "totals"]}`         |
| `periods` | números de periodo | `{"periods": [0]}` — solo tiempo reglamentario |

Una nueva suscripción reemplaza la anterior y devuelve un snapshot actualizado. `{"op": "unsubscribe"}` le devuelve al libro completo, y `{"op": "ping"}` responde `{"t": "pong"}`.

## Mensajes del servidor

| `t`        | Contenido                                       | Significado                                                                        |
| ---------- | ----------------------------------------------- | ---------------------------------------------------------------------------------- |
| `hello`    | `{ subscription }`                              | Enviado al conectar y tras cada cambio de suscripción; refleja lo que está activo. |
| `snapshot` | `{ count, rows }`                               | Estado completo **de su suscripción**, enviado justo después de suscribirse.       |
| `delta`    | `{ rows, specials, removed, removed_specials }` | Lo que cambió desde el mensaje anterior — y lo que desapareció.                    |
| `pong`     | `{ ts }`                                        | Respuesta a `ping`.                                                                |

Cada delta lleva además un `seq`, incrementado en cada difusión. Un cliente sin suscripción recibe todos los números seguidos: un hueco indica un mensaje perdido. Un cliente suscrito a un ámbito verá huecos con normalidad — las difusiones que no le concernían.

## Supresiones

`removed` y `removed_specials` enumeran las series que ya no existen: un partido terminado, un mercado cerrado. **Bórrelas de su libro.** Un cliente que las ignora sigue sirviendo cuotas de mercados desaparecidos: medido sobre nueve horas, eso supone alrededor del 2,5 % del libro, y no deja de crecer.

Una entrada suprimida solo lleva lo necesario para identificar la serie, sin precios: `event_id`, `period`, `market`, `line` para las cuotas; `event_id`, `special_id`, `period`, `contestant_name`, `handicap` para los specials. Se filtran por su suscripción como todo lo demás.

Las líneas tienen la misma forma que en `/api/odds`: `event_id`, `period`, `market`, `line`, las cuotas (`odds1`/`odds0`/`odds2`), las cuotas justas (`todds*`), `status` y `timestamp` — la hora del **último cambio de precio**, no la del envío.

## Por qué suscribirse

El filtrado no es solo comodidad. Medido sobre el libro en directo, el fútbol solo representa alrededor del 40 % de los movimientos, el moneyline a tiempo reglamentario alrededor del 49 %, y un partido aislado es insignificante. Un cliente que sigue unos pocos partidos pasa de decenas de miles de líneas por hora a unas pocas decenas.

<Tip>
  Como el flujo SSE, la conexión cuenta como **1 petición**, en el momento de conectar. Aquello a lo que se suscribe cambia el volumen recibido, no el precio.
</Tip>

<Note>
  Las líneas de cuotas llevan `event_id`, `period` y `market`, pero no el deporte ni la liga — estos se resuelven mediante los fixtures. Un evento ausente del índice de fixtures no cumplirá un filtro `sports` o `leagues`; filtre por `events` si lo necesita de inmediato.
</Note>
