> ## 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.

# Flux WebSocket (abonnements)

> Abonnez-vous aux sports, ligues, événements ou marchés que vous suivez réellement, et ne recevez que ceux-là.

<Note>
  Les flux temps réel nécessitent le forfait **Sharp**.
</Note>

`wss://api.apinn.io/ws` pousse les mouvements de cotes comme le [flux SSE](/concepts/streaming), avec une différence qui compte : la connexion est **bidirectionnelle**, vous pouvez donc dire au serveur ce que vous voulez. Sans abonnement, vous recevez tout le book — exactement ce qu'envoie le flux SSE.

Transmettez votre clé dans l'en-tête `X-API-Key`, ou en `?key=VOTRE_CLE` dans l'URL (un navigateur ne peut pas poser d'en-tête sur une connexion WebSocket).

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

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

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") {
    // état complet et courant de votre abonnement
    console.log(m.count, "lignes");
  } else if (m.t === "delta") {
    // uniquement les lignes qui ont changé, dans votre périmètre
    for (const row of m.rows) console.log(row.event_id, row.market, row.odds1, row.odds2);
  }
});
```

## S'abonner

Envoyez `{"op": "subscribe", ...}` avec n'importe quelle combinaison des filtres ci-dessous. Ils se cumulent : une ligne doit satisfaire tous les filtres que vous posez.

| Filtre    | Type                     | Exemple                                       |
| --------- | ------------------------ | --------------------------------------------- |
| `sports`  | identifiants de sport    | `{"sports": [29, 33]}`                        |
| `leagues` | identifiants de ligue    | `{"leagues": [2436]}`                         |
| `events`  | identifiants d'événement | `{"events": [1632682788]}`                    |
| `markets` | noms de marché           | `{"markets": ["moneyline", "totals"]}`        |
| `periods` | numéros de période       | `{"periods": [0]}` — match complet uniquement |

Un nouvel abonnement remplace le précédent et renvoie un snapshot à jour. `{"op": "unsubscribe"}` vous ramène au book complet, et `{"op": "ping"}` répond `{"t": "pong"}`.

## Messages du serveur

| `t`        | Contenu                                         | Signification                                                                              |
| ---------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `hello`    | `{ subscription }`                              | Envoyé à la connexion et après chaque changement d'abonnement ; rappelle ce qui est actif. |
| `snapshot` | `{ count, rows }`                               | État complet **de votre abonnement**, envoyé juste après la souscription.                  |
| `delta`    | `{ rows, specials, removed, removed_specials }` | Ce qui a changé depuis le message précédent — et ce qui a disparu.                         |
| `pong`     | `{ ts }`                                        | Réponse à `ping`.                                                                          |

Chaque delta porte aussi un `seq`, incrémenté à chaque diffusion. Un client sans abonnement reçoit tous les numéros à la suite : un trou signale un message perdu. Un client abonné à un périmètre verra normalement des trous — les diffusions qui ne le concernaient pas.

## Suppressions

`removed` et `removed_specials` listent les séries qui n'existent plus : un match terminé, un marché fermé. **Effacez-les de votre book.** Un client qui les ignore continue de servir des cotes sur des marchés disparus : mesuré sur neuf heures, cela représente environ 2,5 % du book, et cela ne fait que croître.

Une entrée supprimée ne porte que de quoi identifier la série, pas de prix : `event_id`, `period`, `market`, `line` pour les cotes ; `event_id`, `special_id`, `period`, `contestant_name`, `handicap` pour les specials. Elles sont filtrées par votre abonnement comme le reste.

Les lignes ont la même forme que sur `/api/odds` : `event_id`, `period`, `market`, `line`, les cotes (`odds1`/`odds0`/`odds2`), les cotes justes (`todds*`), `status` et `timestamp` — l'heure du **dernier changement de prix**, pas celle de l'envoi.

## Pourquoi s'abonner

Le filtrage n'est pas un simple confort. Mesuré sur le book en direct, le football seul représente environ 40 % des mouvements, le moneyline en temps réglementaire environ 49 %, et un match isolé est négligeable. Un client qui suit une poignée de matchs passe de dizaines de milliers de lignes par heure à quelques dizaines.

<Tip>
  Comme le flux SSE, la connexion compte pour **1 requête**, au moment où vous vous connectez. Ce à quoi vous vous abonnez change le volume reçu, pas le prix.
</Tip>

<Note>
  Les lignes de cotes portent `event_id`, `period` et `market`, mais ni le sport ni la ligue — ceux-ci sont résolus via les fixtures. Un événement absent de l'index des fixtures ne satisfera pas un filtre `sports` ou `leagues` ; filtrez sur `events` si vous en avez besoin immédiatement.
</Note>
