> For the complete documentation index, see [llms.txt](https://info.soccerfootball.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://info.soccerfootball.info/websocket/channels/stats-realtime.md).

# STATS REALTIME

The STATS\_REALTIME channel delivers only the statistics that have changed, in real-time.

### Availability

| Plan  | Available |
| ----- | --------- |
| BASIC | No        |
| PRO   | No        |
| ULTRA | No        |
| MEGA  | Yes       |

### Update Frequency

Updates are sent **immediately** when statistics change (real-time).

### Events

| Event                   | Description             |
| ----------------------- | ----------------------- |
| stats\_update\_realtime | Changed statistics only |

***

### stats\_update\_realtime

Delivers only the statistics that have changed since the last update.

#### Message

```json
{
  "type": "stats_update_realtime",
  "channel": "STATS_REALTIME",
  "matchId": "12345",
  "leagueId": "67890",
  "ts": 1705314600000,
  "data": {
    "changedStats": [
      {
        "stat": "possession",
        "value": "56;44"
      },
      {
        "stat": "corners",
        "value": "5;2"
      },
      {
        "stat": "shotsOnTarget",
        "value": "5;2"
      }
    ]
  }
}
```

#### Data Fields

| Field                  | Type   | Description                       |
| ---------------------- | ------ | --------------------------------- |
| `changedStats`         | array  | Array of changed statistics       |
| `changedStats[].stat`  | string | Statistic name                    |
| `changedStats[].value` | string | New value in "teamA;teamB" format |

#### Available Statistics

| Stat Name          | Description          |
| ------------------ | -------------------- |
| `timer`            | Current match minute |
| `extraTimer`       | Extra time minutes   |
| `status`           | Match status         |
| `score`            | Current score        |
| `score1h`          | First half score     |
| `score2h`          | Second half score    |
| `possession`       | Ball possession %    |
| `attacks`          | Total attacks        |
| `dangerousAttacks` | Dangerous attacks    |
| `shotsOnTarget`    | Shots on target      |
| `shotsOffTarget`   | Shots off target     |
| `totalShots`       | Total shots          |
| `shotsBlocked`     | Blocked shots        |
| `corners`          | Total corners        |
| `corners1h`        | First half corners   |
| `fouls`            | Fouls committed      |
| `yellowCards`      | Yellow cards         |
| `redCards`         | Red cards            |
| `yellowToRed`      | Second yellow cards  |
| `substitutions`    | Substitutions        |
| `throwIns`         | Throw-ins            |
| `injuries`         | Injuries             |
| `penalties`        | Penalties awarded    |
| `offsides`         | Offsides             |
| `xg`               | Expected goals       |

***

### Subscription Examples

#### Subscribe to all real-time stats updates

NO scope "all" allowed

#### Subscribe to a specific match

```json
{"action": "subscribe", "channel": "STATS_REALTIME", "scope": "match", "id": "b01d28ac92221034"}
```

#### Subscribe to a league

```json
{"action": "subscribe", "channel": "STATS_REALTIME", "scope": "league", "id": "f2b16fa54cb525d"}
```

***

### Example: Maintaining Local State

```javascript
// Local state for match statistics
const matchStats = {};

function initializeStats(matchId) {
  matchStats[matchId] = {
    timer: '0',
    possession: '50;50',
    corners: '0;0',
    shotsOnTarget: '0;0',
    shotsOffTarget: '0;0',
    fouls: '0;0',
    yellowCards: '0;0',
    redCards: '0;0'
    // ... other stats initialized to defaults
  };
}

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type !== 'stats_update_realtime') return;

  const matchId = msg.matchId;

  // Initialize if first update for this match
  if (!matchStats[matchId]) {
    initializeStats(matchId);
  }

  // Apply delta updates
  msg.data.changedStats.forEach(change => {
    const oldValue = matchStats[matchId][change.stat];
    matchStats[matchId][change.stat] = change.value;

    console.log(`[${matchId}] ${change.stat}: ${oldValue} → ${change.value}`);
  });

  // Now matchStats[matchId] contains the current state
  console.log('Current stats:', matchStats[matchId]);
};
```

### Example: Detecting Specific Changes

```javascript
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type !== 'stats_update_realtime') return;

  msg.data.changedStats.forEach(change => {
    switch (change.stat) {
      case 'corners':
        const [homeCorners, awayCorners] = change.value.split(';').map(Number);
        console.log(`Corner! Home: ${homeCorners}, Away: ${awayCorners}`);
        break;

      case 'yellowCards':
        const [homeYellow, awayYellow] = change.value.split(';').map(Number);
        console.log(`Yellow card! Home: ${homeYellow}, Away: ${awayYellow}`);
        break;

      case 'redCards':
        const [homeRed, awayRed] = change.value.split(';').map(Number);
        if (homeRed > 0 || awayRed > 0) {
          console.log(`RED CARD! Home: ${homeRed}, Away: ${awayRed}`);
        }
        break;

      case 'possession':
        const [homePoss, awayPoss] = change.value.split(';').map(Number);
        console.log(`Possession update: ${homePoss}% - ${awayPoss}%`);
        break;
    }
  });
};
```

***

### Comparison: STATS vs STATS\_REALTIME

| Feature           | STATS         | STATS\_REALTIME |
| ----------------- | ------------- | --------------- |
| Update frequency  | 30-60 seconds | Real-time       |
| Data sent         | Full snapshot | Delta only      |
| Bandwidth usage   | Higher        | Lower           |
| State management  | Not required  | Required        |
| Plan availability | ULTRA, MEGA   | MEGA only       |

### Best Practices

1. **Maintain local state**: Keep a local copy of statistics and apply deltas
2. **Handle reconnections**: On reconnect, subscribe to STATS first for a full snapshot, then switch to STATS\_REALTIME
3. **Combine channels**: Use STATS for initial state and STATS\_REALTIME for updates
4. **Handle missing matches**: Initialize stats when you receive the first update for a match

### Notes

* Only changed statistics are included in each message
* An empty `changedStats` array means no changes (rare, but possible)
* For a complete snapshot, use the STATS channel
* This channel requires client-side state management


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://info.soccerfootball.info/websocket/channels/stats-realtime.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
