# STATS

## STATS Channel

The STATS channel delivers complete statistics snapshots periodically during live matches.

### Availability

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

### Update Frequency

Update frequency depends on your subscription scope:

* **`match` / `league` scope:** statistics are delivered **immediately** when data changes
* **`all` scope:** statistics are accumulated and delivered as a **batch every 30 seconds** (`batch_update`)

### Events

| Event         | Scope             | Description                                    |
| ------------- | ----------------- | ---------------------------------------------- |
| stats\_update | `match`, `league` | Full statistics snapshot (immediate)           |
| batch\_update | `all`             | Batched statistics for all matches (every 30s) |

***

### stats\_update

Delivers a complete snapshot of all match statistics.

#### Message

```json
{
  "type": "event",
  "channel": "STATS",
  "scope": "match",
  "id": "12345",
  "eventType": "stats_update",
  "eventId": "abc123",
  "ts": 1705314600000,
  "data": {
    "timer": "35",
    "extraTimer": null,
    "status": "IN_PLAY",
    "score": "1;0",
    "score1h": null,
    "score2h": null,
    "possession": "55;45",
    "attacks": "48;32",
    "dangerousAttacks": "22;15",
    "shotsOnTarget": "4;2",
    "shotsOffTarget": "3;5",
    "totalShots": "7;7",
    "shotsBlocked": "2;1",
    "corners": "4;2",
    "corners1h": null,
    "fouls": "8;10",
    "yellowCards": "1;2",
    "redCards": "0;0",
    "yellowToRed": "0;0",
    "substitutions": "0;1",
    "throwIns": "12;9",
    "injuries": "0;1",
    "penalties": "0;0",
    "offsides": "2;1",
    "xg": "0.85;0.42"
  }
}
```

#### Data Fields

**Match State**

| Field        | Type        | Description                             |
| ------------ | ----------- | --------------------------------------- |
| `timer`      | string      | Current match minute                    |
| `extraTimer` | string/null | Extra time minutes (e.g., "3" for 45+3) |
| `status`     | string      | Match status                            |
| `score`      | string      | Current score "teamA;teamB"             |
| `score1h`    | string/null | First half score (after halftime)       |
| `score2h`    | string/null | Second half score (after full time)     |

**Statistics**

All statistics are in format `"teamA;teamB"`:

| Field              | Type        | Description                         |
| ------------------ | ----------- | ----------------------------------- |
| `possession`       | string      | Ball possession percentage          |
| `attacks`          | string      | Total attacks                       |
| `dangerousAttacks` | string      | Dangerous attacks                   |
| `shotsOnTarget`    | string      | Shots on target                     |
| `shotsOffTarget`   | string      | Shots off target                    |
| `totalShots`       | string      | Total shots                         |
| `shotsBlocked`     | string      | Blocked shots                       |
| `corners`          | string      | Total corners                       |
| `corners1h`        | string/null | First half corners                  |
| `fouls`            | string      | Fouls committed                     |
| `yellowCards`      | string      | Yellow cards                        |
| `redCards`         | string      | Red cards                           |
| `yellowToRed`      | string      | Second yellow (yellow to red) cards |
| `substitutions`    | string      | Substitutions made                  |
| `throwIns`         | string      | Throw-ins                           |
| `injuries`         | string      | Injuries                            |
| `penalties`        | string      | Penalties awarded                   |
| `offsides`         | string      | Offsides                            |
| `xg`               | string/null | Expected goals (xG)                 |

#### Match Status Values

| Status        | Description             |
| ------------- | ----------------------- |
| `NOT_STARTED` | Match hasn't started    |
| `IN_PLAY`     | First half in progress  |
| `HALFTIME`    | Halftime break          |
| `IN_PLAY_2H`  | Second half in progress |
| `EXTRA_TIME`  | Extra time in progress  |
| `PENALTIES`   | Penalty shootout        |
| `ENDED`       | Match finished          |
| `POSTPONED`   | Match postponed         |
| `CANCELLED`   | Match cancelled         |

***

### batch\_update

Delivers a batch of statistics snapshots for all live matches. Sent every 30 seconds to `all` scope subscribers only.

#### Message

```json
{
  "type": "batch_update",
  "channel": "STATS",
  "scope": "all",
  "timestamp": 1705314600000,
  "matches": [
    {
      "matchId": "12345",
      "leagueId": "67890",
      "data": {
        "timer": "35",
        "status": "IN_PLAY",
        "score": "1;0",
        "possession": "55;45",
        "..."
      }
    },
    {
      "matchId": "12346",
      "leagueId": "67890",
      "data": {
        "timer": "72",
        "status": "IN_PLAY_2H",
        "score": "2;2",
        "possession": "48;52",
        "..."
      }
    }
  ]
}
```

#### Fields

| Field                | Type    | Description                                              |
| -------------------- | ------- | -------------------------------------------------------- |
| `type`               | string  | Always `"batch_update"`                                  |
| `channel`            | string  | Always `"STATS"`                                         |
| `scope`              | string  | Always `"all"`                                           |
| `timestamp`          | integer | Batch creation timestamp in milliseconds                 |
| `matches`            | array   | Array of match updates accumulated since last batch      |
| `matches[].matchId`  | string  | Match public ID                                          |
| `matches[].leagueId` | string  | League/Championship public ID                            |
| `matches[].data`     | object  | Statistics snapshot (same fields as `stats_update` data) |

> **Note:** If a match has multiple updates within a 30-second window, only the latest data for that `matchId` is included in the batch.

***

### Subscription Examples

#### Subscribe to all stats updates

```json
{"action": "subscribe", "channel": "STATS", "scope": "all"}
```

#### Subscribe to a specific match

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

#### Subscribe to a league

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

***

### Example: Processing Stats

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

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

  const stats = msg.data;

  // Parse possession
  const [homePoss, awayPoss] = stats.possession.split(';').map(Number);
  console.log(`Possession: Home ${homePoss}% - Away ${awayPoss}%`);

  // Parse shots
  const [homeShots, awayShots] = stats.totalShots.split(';').map(Number);
  const [homeSoT, awaySoT] = stats.shotsOnTarget.split(';').map(Number);
  console.log(`Shots: Home ${homeShots} (${homeSoT} on target) - Away ${awayShots} (${awaySoT} on target)`);

  // Parse corners
  const [homeCorners, awayCorners] = stats.corners.split(';').map(Number);
  console.log(`Corners: Home ${homeCorners} - Away ${awayCorners}`);

  // Parse xG if available
  if (stats.xg) {
    const [homeXg, awayXg] = stats.xg.split(';').map(parseFloat);
    console.log(`xG: Home ${homeXg.toFixed(2)} - Away ${awayXg.toFixed(2)}`);
  }
};
```

### Example: Building a Stats Display

```javascript
function formatStats(stats) {
  const parseValue = (val) => val ? val.split(';') : ['-', '-'];

  return {
    timer: `${stats.timer}'${stats.extraTimer ? `+${stats.extraTimer}` : ''}`,
    score: parseValue(stats.score).join(' - '),
    possession: {
      home: parseValue(stats.possession)[0] + '%',
      away: parseValue(stats.possession)[1] + '%'
    },
    shots: {
      home: parseValue(stats.totalShots)[0],
      away: parseValue(stats.totalShots)[1]
    },
    shotsOnTarget: {
      home: parseValue(stats.shotsOnTarget)[0],
      away: parseValue(stats.shotsOnTarget)[1]
    },
    corners: {
      home: parseValue(stats.corners)[0],
      away: parseValue(stats.corners)[1]
    },
    fouls: {
      home: parseValue(stats.fouls)[0],
      away: parseValue(stats.fouls)[1]
    },
    cards: {
      home: `${parseValue(stats.yellowCards)[0]}Y ${parseValue(stats.redCards)[0]}R`,
      away: `${parseValue(stats.yellowCards)[1]}Y ${parseValue(stats.redCards)[1]}R`
    }
  };
}
```

***

### Notes

* Statistics are only sent while the match is live
* Null values indicate the statistic is not yet available
* The snapshot includes ALL available statistics, not just changed values
* For real-time delta updates, use the STATS\_REALTIME channel (MEGA plan only)


---

# Agent Instructions: 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:

```
GET https://info.soccerfootball.info/websocket/channels/stats.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
