Skip to main content
Real-time streams require the Sharp plan.
wss://api.apinn.io/ws pushes odds movements like the SSE stream, with one difference that matters: the connection is two-way, so you can tell the server what you want. Without a subscription you receive the whole book — exactly what the SSE stream sends. Pass your key in the X-API-Key header, or as ?key=YOUR_KEY in the URL (a browser cannot set a header on a WebSocket connection).
Node.js

Subscribing

Send {"op": "subscribe", ...} with any combination of the filters below. They are cumulative: a line must match all the filters you set. Subscribing again replaces the previous subscription and returns a fresh snapshot. {"op": "unsubscribe"} returns you to the full book, and {"op": "ping"} answers {"t": "pong"}.

Server messages

Every delta also carries a seq, incremented once per broadcast. A client with no subscription receives every number in order, so a gap means a lost message; a client with a subscription will normally see gaps (the broadcasts that did not concern it).

Removals

removed and removed_specials list the series that no longer exist — a match that ended, a market that closed. Delete them from your book. A client that ignores them keeps serving odds on markets that are gone: measured over nine hours, that is around 2.5% of the book, and it grows. Removed entries carry only what identifies the series, not prices: event_id, period, market, line for odds; event_id, special_id, period, contestant_name, handicap for specials. They are filtered by your subscription like everything else. Rows use the same shape as /api/odds: event_id, period, market, line, the odds (odds1/odds0/odds2), the true odds (todds*), status, and timestamp — the time of the last price change, not the time we sent it.

Why subscribe

Filtering is not just convenience. Measured on the live book, football alone is around 40% of all movements, moneyline on full time around 49%, and a single match is a rounding error. A client that follows a handful of matches goes from tens of thousands of lines an hour to a few dozen.
Like the SSE stream, the connection counts as 1 request, at the moment you connect. What you subscribe to changes the volume you receive, not the price.
Odds lines carry event_id, period and market, but not the sport or the league — those are resolved through the fixtures. An event that is not yet in the fixtures index will not match a sports or leagues filter; filter on events if you need it immediately.