Real-time(Socket.IO) Integration

Getting started

Real-time push updates are available via Socket.IO. Subscribe to live events, live odds, and sport-wide feeds without polling. This page provides complete integration examples for all major frameworks.

âš ī¸
Token Authentication Required:Socket.IO connections now require a special token obtained from the /ws-token endpoint. This token is only available to MEGA plan subscribers. See the Token Authentication section below.
â„šī¸
TipSocket.IO URL: https://live.matchstat.com
Rapid HOST: tennis-api-atp-wta-itf.p.rapidapi.com tennis-live-api.p.rapidapi.com

Note: Use the RapidAPI host corresponding to the product for which you purchased the Mega plan.

Why Token ? --> RapidAPI doesn't natively support WebSocket authentication, so we use this token-based system to verify MEGA plan access.

Tennis Live API WebSocket — Events Reference

This page is the single source of truth for every event exchanged over the Tennis Live API WebSocket connection. It is language-agnostic: once you have an authenticated socket connection open (see the Quick Start / language-specific integration guides), use this table to know what to emit, what to listen for, and what shape to expect back — without needing a separate code sample per language for every event.

â„šī¸
How to use this reference:

Connect using the Quick Start snippet for your language, then simply emit/on the event names above. Every language-specific integration guide (React/Next.js, C#/.NET, etc.) points back to this page instead of re-documenting payload shapes, so this table is the only place that needs updating when the event contract changes.

Connection Lifecycle Events

These fire automatically as part of the underlying socket connection and don't need to be joined or left explicitly.

EventDirectionPayloadDescription
connectServer → Client—Fired once the socket has connected and the token was accepted. The connection ID is the client-side property socket.id; the server sends no payload.
disconnectServer → Clientreason: stringFired when the socket disconnects. reason is the standard Socket.IO reason, passed through unchanged (e.g. transport close, ping timeout, io server disconnect, io client disconnect).
connect_errorServer → Client{ message: string }Fired when the handshake is rejected. The error carries only message, one of:
  • Required token. — no auth.token was sent.
  • TOKEN_EXPIRED — the token is older than 24 hours: call /ws-token again and reconnect. The token is checked only when connecting: an open connection is not closed when its token expires, but the next reconnect needs a fresh token.
  • AUTH_FAILED — the token is invalid.
  • Higher plan required. — the token was not issued for a MEGA plan.
ping-server / pong-serverClient → Server / Server → Client—Optional application-level heartbeat: emit ping-server, the server answers pong-server.

Single-Event Subscription (Live Score & Odds)

Used when a client wants live updates for one specific match/event.

EventDirectionPayloadDescription
join-eventClient → ServereventId: numberSubscribes the socket to updates for one live event (the live id from /events/live). Fire-and-forget: the server sends no acknowledgement or confirmation event. Join only events whose status is InPlay — that check is done by your client; it is not part of the payload.
leave-eventClient → ServereventId: numberUnsubscribes from that event. Fire-and-forget (no acknowledgement). Call it on cleanup.
event-updateServer → ClientThe full live event row (same fields as one item of GET /events/live)Sent when the event changes while InPlay (point, game, set, status). The server is shown by indicator ("1,0" = player 1 serving); set scores are in score (comma-separated sets).
odds-updateServer → Client{ results: { "Full Time Result": { od1, od1Bookmaker, od2, od2Bookmaker, odx, odxBookmaker, line, updatedAt } } }Sent when new odds arrive for the event: the best match-winner price per outcome across bookmakers (same shape as GET /event/odds/best/{eventId}). Odds are decimal strings; updatedAt is Unix seconds. The payload does not include the event ID: see the note below.
// event-update (real payload shape)
{
  "id": "4071379",
  "name": "Asahi Harazaki vs Jonah Braswell",
  "participant1": "Asahi Harazaki",
  "participant2": "Jonah Braswell",
  "league": "M15 Fayetteville",
  "score": "6-4,0-0",
  "status": "InPlay",
  "points": "40-40",
  "indicator": "1,0",
  "player1": { "name": "Asahi Harazaki" },
  "player2": { "name": "Jonah Braswell" },
  "tournament": { "name": "M15 Fayetteville" },
  "tourType": "atp",
  "startTimestamp": 1790776800,
  "matchId": "92049-94972-22090-4"
}

// odds-update (real payload shape)
{
  "results": {
    "Full Time Result": {
      "od1": "1.72", "od1Bookmaker": "Pinnacle",
      "od2": "2.25", "od2Bookmaker": "Bet365",
      "odx": null, "odxBookmaker": null,
      "line": null,
      "updatedAt": 1790777402
    }
  }
}

Field meanings are the same as in REST: see Live Events (event fields) and Best Live Odds(odds fields). The event's matchId equals the liveMatchId of the same match in Upcoming Matches rows, and GET /matches/{matchId}/live returns its live eventId.

âš ī¸
Reconnects and several matches

Rejoin after every reconnect. Subscriptions (rooms) belong to one connection. When the connection drops and Socket.IO reconnects, the server has forgotten them: emit join-event / join-live-events-all again on every connect. A disconnect started by the server (io server disconnect) is not retried by Socket.IO itself: call socket.connect().

Attributing odds. event-update carries the event id, so it can be routed to the right match. odds-update does not: on a connection that follows several events, an odds payload cannot be matched to its event. Follow one event per connection for odds, or poll GET /event/odds/best/{eventId} for each match.

The integration guides below implement both (one shared connection, rejoin on connect, per-event subscriber counts, no retry on plan or token errors).

All-Matches Subscription

Used when a client wants a single feed covering every in-play match for a sport, rather than subscribing per match.

EventDirectionPayloadDescription
join-live-events-allClient → ServerThe plain string "tennis", e.g. socket.emit("join-live-events-all", "tennis"). Not an object: {"sportSlug":"tennis"} is ignored with no error.Subscribes the socket to the feed of every live tennis match (ATP, WTA, Challenger and ITF are all inside it; tour is in each item's tourType). Fire-and-forget: there is no acknowledgement; the first live-events-all-update snapshot arrives about 2 seconds after joining and serves as the initial state.
leave-live-events-allClient → ServersportSlug: stringUnsubscribes from the combined feed. Always call this on cleanup/disposal.
live-events-all-updateServer → Client{ count: number, results: LiveEvent[] }A full snapshot of all live events (each item = one /events/live row), sent on every live refresh. Not a diff: replace your list with results.

Code example