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

# WebSockets

> Build real-time padel scoreboards and live trackers with the Padel API: REST polling, WebSocket subscriptions, and working examples.

Track professional padel matches point-by-point as they happen. The Padel API provides two ways to get live data: a REST endpoint for polling and WebSocket channels for instant push updates.

<CardGroup cols={2}>
  <Card title="REST Polling" icon="arrows-rotate">
    Fetch the latest match state on demand. Simple to implement, works everywhere.
  </Card>

  <Card title="WebSockets" icon="bolt">
    Receive updates instantly via Pusher. No polling, no delays.
  </Card>
</CardGroup>

<Note>
  Looking for *resource changes* rather than point-by-point data? Use [Webhooks](/docs/webhooks) instead — they push notifications when a tournament, match, or player is created, updated, or deleted, without keeping a connection open.
</Note>

## Prerequisites

Before you begin, make sure you have:

* A Padel API account ([sign up here](https://padelapi.org/register))
* An API token from your [API Tokens](https://padelapi.org/user/api-tokens) page

***

## Step 1: Find Live Matches

First, discover which matches are currently being played using [`GET /api/live`](/docs/api-reference/live/list-live-matches):

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    'https://padelapi.org/api/live' \
    -H 'Authorization: Bearer YOUR_API_TOKEN'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://padelapi.org/api/live', {
    headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
  });

  const liveMatches = await response.json();
  console.log(liveMatches.data);
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://padelapi.org/api/live',
      headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
  )

  live_matches = response.json()
  print(live_matches['data'])
  ```
</CodeGroup>

This returns a list of all matches with `status: "live"`. Each match includes its `id`, which you'll use in the next steps.

***

## Step 2: Get the Live Match State

Once you have a match ID, fetch its point-by-point data with [`GET /api/matches/{id}/live`](/docs/api-reference/match/show-match-point-by-point):

```bash theme={null}
curl -X GET \
  'https://padelapi.org/api/matches/7243/live' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

### Response structure

```json theme={null}
{
  "id": 7243,
  "self": "/api/matches/7243/live",
  "status": "live",
  "coverage": "full",
  "channel": "matches.7243",
  "sets": [
    {
      "set_number": 1,
      "set_score": "6-2",
      "games": [
        {
          "game_number": 1,
          "game_score": "0 - 0",
          "serving": "team_1",
          "points": ["15:0", "30:0", "40:15"]
        },
        {
          "game_number": 2,
          "game_score": "1 - 0",
          "serving": "team_2",
          "points": ["0:0", "15:0", "30:0", "40:0", "40:15"]
        }
      ]
    },
    {
      "set_number": 2,
      "set_score": null,
      "games": [
        {
          "game_number": 1,
          "game_score": "0 - 0",
          "serving": "team_1",
          "points": ["0:0", "15:0", "30:0"]
        }
      ]
    }
  ],
  "connections": {
    "match": "/api/matches/7243"
  }
}
```

### Field reference

| Field                        | Description                                                                                                                             |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                     | Match status in a live context: `live`, `ended`, or `finished`. See [Match Statuses](/docs/statuses#match-statuses) for all possible values. |
| `coverage`                   | Data completeness: `tracking` (collecting data), `full` (all games tracked), `partial` (some gaps), or `null` (no data)                 |
| `channel`                    | Pusher channel name for real-time updates                                                                                               |
| `sets[].set_number`          | Set number (1, 2, or 3)                                                                                                                 |
| `sets[].set_score`           | Final set score (e.g. `"6-2"`) when complete, `null` while in progress                                                                  |
| `sets[].games[].game_number` | Game number within the set                                                                                                              |
| `sets[].games[].game_score`  | Cumulative set score **before** this game started (e.g. `"3 - 1"`)                                                                      |
| `sets[].games[].serving`     | Serving team: `"team_1"`, `"team_2"`, or `null` if unknown                                                                              |
| `sets[].games[].points`      | Array of point scores during the game (format `"team1:team2"`)                                                                          |

<Tip>
  The `channel` field is the exact Pusher channel name you need for Step 3 — use it directly, no need to construct it manually.
</Tip>

***

## Step 3: Subscribe to Real-Time Updates

Instead of polling the REST endpoint repeatedly, subscribe to WebSocket updates for instant notifications on every point.

### Connection details

| Parameter          | Value                                  |
| ------------------ | -------------------------------------- |
| **Provider**       | [Pusher](https://pusher.com)           |
| **App Key**        | `0ffbefeb945e4e466065`                 |
| **Cluster**        | `eu`                                   |
| **Channel format** | `matches.{matchId}`                    |
| **Event name**     | `App\PadelApi\Events\MatchLiveUpdated` |

<Note>
  Channels are **public** — no authentication handshake is required to subscribe. You only need the Pusher app key and cluster.
</Note>

### Install a Pusher client

<CodeGroup>
  ```bash npm theme={null}
  npm install pusher-js
  ```

  ```bash pip theme={null}
  pip install pysher
  ```
</CodeGroup>

### Subscribe to a match channel

<CodeGroup>
  ```javascript JavaScript theme={null}
  import Pusher from 'pusher-js';

  const pusher = new Pusher('0ffbefeb945e4e466065', {
    cluster: 'eu'
  });

  // Subscribe using the "channel" field from the live endpoint
  const channel = pusher.subscribe('matches.7243');

  channel.bind('App\\PadelApi\\Events\\MatchLiveUpdated', (data) => {
    console.log('Match update:', data.status);
    console.log('Sets:', data.sets);
    // data has the same structure as the /live REST response
  });
  ```

  ```python Python theme={null}
  import pysher
  import json

  def on_event(data):
      match = json.loads(data)
      print(f"Match update: {match['status']}")
      print(f"Sets: {match['sets']}")

  pusher = pysher.Pusher(key='0ffbefeb945e4e466065', cluster='eu')
  pusher.connection.bind('pusher:connection_established', lambda _: None)
  pusher.connect()

  channel = pusher.subscribe('matches.7243')
  channel.bind('App\\PadelApi\\Events\\MatchLiveUpdated', on_event)
  ```
</CodeGroup>

The WebSocket event payload has the **same structure** as the REST response — `id`, `self`, `status`, `coverage`, `channel`, `sets`, and `connections`.

***

## Step 4: Handle Match Completion

When a match ends, you will receive two final events in sequence:

1. **`ended`** — the match is over, but results are still being confirmed
2. **`finished`** — the match is fully closed and results are final

See [Match Statuses](/docs/statuses#match-statuses) for details. You can react to either status depending on your use case — unsubscribe from the channel once you receive `finished`:

<CodeGroup>
  ```javascript JavaScript theme={null}
  channel.bind('App\\PadelApi\\Events\\MatchLiveUpdated', (data) => {
    updateScoreboard(data);

    if (data.status === 'ended') {
      console.log('Match ended, waiting for final confirmation...');
    }

    if (data.status === 'finished') {
      pusher.unsubscribe('matches.7243');
      console.log('Match finished, unsubscribed');
    }
  });
  ```

  ```python Python theme={null}
  def on_event(data):
      match = json.loads(data)
      update_scoreboard(match)

      if match['status'] == 'ended':
          print('Match ended, waiting for final confirmation...')

      if match['status'] == 'finished':
          pusher.unsubscribe('matches.7243')
          print('Match finished, unsubscribed')
  ```
</CodeGroup>

***

## Recommended Workflow

1. **Find live matches** — [`GET /api/live`](/docs/api-reference/live/list-live-matches)
2. **Get initial state** — [`GET /api/matches/{id}/live`](/docs/api-reference/match/show-match-point-by-point)
3. **Subscribe to updates** — Connect to Pusher channel `matches.{id}`
4. **Detect match end** — When `status` changes to `ended` (match over) and then `finished` (results confirmed), unsubscribe from the channel

***

## Tips

<AccordionGroup>
  <Accordion title="REST polling vs WebSockets vs Webhooks — when to use which">
    Use **REST polling** when you need a simple integration, are building server-side batch processes, or your environment doesn't support persistent connections. Use **WebSockets** for user-facing applications where latency matters — live scoreboards, mobile apps, or real-time dashboards. Use [**Webhooks**](/docs/webhooks) for backend integrations that react to *resource changes* (match created, score finalised, player merged) — server-to-server, no persistent connection, signed payloads. WebSockets and Webhooks solve different problems: point-by-point during play vs. notifications when something changes.
  </Accordion>

  <Accordion title="Not all matches have live data">
    The `coverage` field indicates whether any points were lost during tracking. `full` means every game was captured and matches the confirmed score, `partial` means some games were missed due to source feed gaps, and `tracking` means the match is live and data is being collected. Coverage refers to game-level completeness — individual points within a game may occasionally be missing.
  </Accordion>

  <Accordion title="Handling reconnections">
    Pusher clients handle reconnections automatically. If the connection drops, the client will reconnect and resubscribe to channels. However, you may miss events during the disconnection — fetch the latest state from the REST endpoint after reconnecting to fill any gaps.
  </Accordion>

  <Accordion title="Rate limits for the REST endpoint">
    If you choose REST polling over WebSockets, avoid polling more frequently than once every 10 seconds per match. For lower latency, use WebSockets instead.
  </Accordion>
</AccordionGroup>

***

## Next steps

<CardGroup cols={3}>
  <Card title="Live Match Endpoint" icon="bolt" href="/docs/api-reference/match/show-match-point-by-point">
    Full reference for the point-by-point endpoint
  </Card>

  <Card title="Live Matches Endpoint" icon="arrows-rotate" href="/docs/api-reference/live/list-live-matches">
    Discover every match currently in progress
  </Card>

  <Card title="Webhooks" icon="webhook" href="/docs/webhooks">
    Server-to-server push for tournament, match, and player changes
  </Card>
</CardGroup>

<CardGroup cols={2}>
  <Card title="Match Statistics" icon="chart-bar" href="/docs/guides/padel-statistics">
    Combine live tracking with post-match stats analysis
  </Card>

  <Card title="API Reference" icon="book" href="/docs/index">
    Full endpoint documentation with parameters and responses
  </Card>
</CardGroup>
