# Snooker API complete reference

Snooker data as JSON: events, draws, results with frame scores, live frame scores, players, world rankings and century breaks, from 1927 to today.

- Base URL: `https://api.snookerapi.net`
- Authentication: the key in `Authorization: Bearer <key>` or `X-API-Key: <key>`, never in the address
- Site: https://snookerapi.net
- Endpoints: 28
- Generated: 2026-10-28 06:06 UTC
- This file: https://snookerapi.net/documentation/manual.md
- Operated by DeveloperData.net. Times and dates are UTC.

## Before anything else

Factual data only: results, scores, draws, rankings and records. No endpoint gives predictions, ratings, tips or betting advice.

Not in the data, on any plan, at any price:

- betting prices of any kind
- shot-by-shot or ball-by-ball detail
- frame scores for every match: they are held where the frames were recorded, mostly finals
- breaks below 50 within a frame
- player photographs and television listings

Every answer is `{"data": ..., "meta": {...}}`. `meta` carries `plan` (the plan the key is on), and on lists `count`, `next_cursor`, and with `page` also `page`, `per_page`, `total`, `total_pages`, `next_page` and `prev_page`. A `note` says when the Free key's window trimmed the answer.

## Plans

| Plan | Price | Reads | Rate limit | Calls |
| --- | --- | --- | --- | --- |
| Free key | Free | Yesterday, today and what is scheduled ahead | 30 a minute | 50 a day |
| Developer | $49 a month or $490 a year | The whole record from 1927 | 60 a minute | 20,000 a month |
| Pro | $149 a month or $1,490 a year | The whole record from 1927, with bulk export | 300 a minute | 200,000 a month |

Prices and plans: https://snookerapi.net/pricing/

## Coverage

Counted from the data when this copy was generated:

- 64,396 matches in 1,192 events, from 1927
- 1,082 matches with frame-by-frame scores (13,873 frames)
- 3,130 players
- 28,030 century breaks
- world ranking lists over 49 seasons

The figures as they stand now: https://snookerapi.net/data/

## Endpoints

### Events

Tournaments and championships, with their dates, venues, champions and prize money.

#### `GET /v1/events`

Events: every tournament and championship, newest first.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `season` | query | season | no | Season, e.g. 2025-26. |  |
| `year` | query | year | no | Calendar year the event started in. |  |
| `tour` | query | string | no | Which tour: amateur is the IBSF, EBSA and ACBS championships and their junior events; q_tour the development tour (the Q Tour and the Challenge Tour before it), a season to an event, its events as rounds. One of: professional, women, seniors, q_school, amateur, q_tour. |  |
| `competition_id` | query | id | no | Only this competition (cp_ id). |  |
| `q` | query | string | no | Words in the event name. | `World Snooker Championship` |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `2` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `name`, `season`, `tour`, `kind`, `competition`, `start_date`, `end_date`, `held_in`, `venue`, `town`, `country`, `location`, `time_zone`, `utc_offset`, `winner`, `runner_up`, `final_score`, `highest_break`, `highest_break_basis`, `prize_fund`, `matches`.

MCP tool: `find_events`.

Sample: `GET https://api.snookerapi.net/v1/events?q=World+Snooker+Championship&per_page=2`

```json
{
    "data": [
        {
            "id": "ev_z867dfpm",
            "name": "2027 World Snooker Championship",
            "season": "2026-27",
            "tour": "professional",
            "kind": "ranking",
            "competition": {
                "id": "cp_19bahdvy",
                "name": "World Snooker Championship"
            },
            "start_date": "2027-04-17",
            "end_date": "2027-05-03",
            "held_in": null,
            "venue": "Crucible Theatre",
            "town": "Sheffield",
            "country": "England",
            "location": {
                "lat": 53.38111,
                "lon": -1.46667,
                "basis": "venue"
            },
            "time_zone": "Europe/London",
            "utc_offset": "+01:00",
            "winner": null,
            "runner_up": null,
            "final_score": null,
            "highest_break": null,
            "highest_break_basis": null,
            "prize_fund": "£3,000,000",
            "matches": 0
        },
        {
            "id": "ev_80r2d1px",
            "name": "2026 World Snooker Championship",
            "season": "2025-26",
            "tour": "professional",
            "kind": "ranking",
            "competition": {
                "id": "cp_19bahdvy",
                "name": "World Snooker Championship"
            },
            "start_date": "2026-04-18",
            "end_date": "2026-05-04",
            "held_in": null,
            "venue": "Crucible Theatre",
            "town": "Sheffield",
            "country": "England",
            "location": {
                "lat": 53.38111,
                "lon": -1.46667,
                "basis": "venue"
            },
            "time_zone": "Europe/London",
            "utc_offset": "+01:00",
            "winner": {
                "id": "pl_ra6m6b4g",
                "name": "Wu Yize"
            },
            "runner_up": {
                "id": "pl_w2c64qam",
                "name": "Shaun Murphy"
            },
            "final_score": "18-17",
            "highest_break": 147,
            "highest_break_basis": "article",
            "prize_fund": "£2,395,000",
            "matches": 143
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 2,
        "next_cursor": "bzIuMjY0ZmE2",
        "page": 1,
        "per_page": 2,
        "total": 96,
        "total_pages": 48,
        "next_page": 2,
        "prev_page": null
    }
}
```

#### `GET /v1/events/{event_id}`

One event: its dates, venue, champion, final, prize fund and its own figures (frames played, deciding frames, whitewashes, centuries).

Plans: every plan; the Free key reads yesterday onwards.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | path | id | yes | The id, as the list calls return it. | `ev_80r2d1px` |

Errors: 401 unauthorized, 403 upgrade_required, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `name`, `season`, `tour`, `kind`, `competition`, `start_date`, `end_date`, `held_in`, `venue`, `town`, `country`, `location`, `time_zone`, `utc_offset`, `winner`, `runner_up`, `final_score`, `highest_break`, `highest_break_basis`, `prize_fund`, `matches`, `prizes`, `stats`, `seeds`.

MCP tool: `get_event`.

Sample: `GET https://api.snookerapi.net/v1/events/ev_80r2d1px`

```json
{
    "data": {
        "id": "ev_80r2d1px",
        "name": "2026 World Snooker Championship",
        "season": "2025-26",
        "tour": "professional",
        "kind": "ranking",
        "competition": {
            "id": "cp_19bahdvy",
            "name": "World Snooker Championship"
        },
        "start_date": "2026-04-18",
        "end_date": "2026-05-04",
        "held_in": null,
        "venue": "Crucible Theatre",
        "town": "Sheffield",
        "country": "England",
        "location": {
            "lat": 53.38111,
            "lon": -1.46667,
            "basis": "venue"
        },
        "time_zone": "Europe/London",
        "utc_offset": "+01:00",
        "winner": {
            "id": "pl_ra6m6b4g",
            "name": "Wu Yize"
        },
        "runner_up": {
            "id": "pl_w2c64qam",
            "name": "Shaun Murphy"
        },
        "final_score": "18-17",
        "highest_break": 147,
        "highest_break_basis": "article",
        "prize_fund": "£2,395,000",
        "matches": 143,
        "prizes": [
            {
                "for": "total",
                "amount": 2395000,
                "currency": "GBP"
            },
            {
                "for": "winner",
                "amount": 500000,
                "currency": "GBP"
            },
            {
                "for": "runner_up",
                "amount": 200000,
                "currency": "GBP"
            },
            {
                "for": "semi_final",
                "amount": 100000,
                "currency": "GBP"
            },
            {
                "for": "quarter_final",
                "amount": 50000,
                "currency": "GBP"
            },
            {
                "for": "last_16",
                "amount": 30000,
                "currency": "GBP"
            },
            {
                "for": "last_32",
                "amount": 20000,
                "currency": "GBP"
            },
            {
                "for": "last_48",
                "amount": 15000,
                "currency": "GBP"
            },
            {
                "for": "last_80",
                "amount": 10000,
                "currency": "GBP"
            },
            {
                "for": "last_112",
                "amount": 5000,
                "currency": "GBP"
            }
        ],
        "stats": {
            "matches_played": 141,
            "frames_played": 2321,
            "deciding_frames": 18,
            "whitewashes": 0,
            "century_breaks": 254
        },
        "seeds": []
    },
    "meta": {
        "plan": "House key"
    }
}
```

#### `GET /v1/events/{event_id}/points`

The world ranking points each player earned at a ranking event, as the season's rankings publish them, and whether our own results for the event give the same points.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | path | id | yes | The id, as the list calls return it. | `ev_gceh5m87` |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `5` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `player`, `points`, `rebuilt_points`, `agrees`.

MCP tool: `event_ranking_points`.

Sample: `GET https://api.snookerapi.net/v1/events/ev_gceh5m87/points?per_page=5`

```json
{
    "data": [
        {
            "player": {
                "id": "pl_7g8hbdzt",
                "name": "Mark Selby"
            },
            "points": 500000,
            "rebuilt_points": 500000,
            "agrees": true
        },
        {
            "player": {
                "id": "pl_w2c64qam",
                "name": "Shaun Murphy"
            },
            "points": 200000,
            "rebuilt_points": 200000,
            "agrees": true
        },
        {
            "player": {
                "id": "pl_wy3wkk0f",
                "name": "Kyren Wilson"
            },
            "points": 100000,
            "rebuilt_points": 100000,
            "agrees": true
        },
        {
            "player": {
                "id": "pl_0gdn9s91",
                "name": "Stuart Bingham"
            },
            "points": 100000,
            "rebuilt_points": 100000,
            "agrees": true
        },
        {
            "player": {
                "id": "pl_0cxvrznr",
                "name": "Anthony McGill"
            },
            "points": 50000,
            "rebuilt_points": 50000,
            "agrees": true
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 5,
        "next_cursor": "bzUuOGY5NjNi",
        "page": 1,
        "per_page": 5,
        "total": 110,
        "total_pages": 22,
        "next_page": 2,
        "prev_page": null,
        "event": {
            "id": "ev_gceh5m87",
            "name": "2021 World Snooker Championship",
            "season": "2020-21"
        },
        "players_agree": "110 of 110"
    }
}
```

### Matches

Results, rounds, dates and frame-by-frame scores.

#### `GET /v1/matches`

Matches: results and draws, newest first, by event, player, season or dates.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). | `ev_80r2d1px` |
| `player_id` | query | id | no | Only this player's matches (pl_ id). |  |
| `season` | query | season | no | Season, e.g. 2025-26. |  |
| `stage` | query | string | no | Main draw or qualifying. One of: main, qualifying, pre_qualifying. |  |
| `round` | query | string | no | Round, e.g. final, semi_final, quarter_final, last_16; league or league_1 for a round robin, group_a for a group, event_1_last_round for a Q School event's deciding round. | `final` |
| `from` | query | date | no | Played on or after this day. |  |
| `to` | query | date | no | Played on or before this day. |  |
| `status` | query | string | no | Only matches in this state: scheduled, in_play, finished, drawn, walkover, incomplete (a score but no result after its day) or unrecorded (its day passed with no score published). One of: scheduled, in_play, finished, drawn, walkover, incomplete, unrecorded. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `event`, `stage`, `round`, `best_of`, `player_one`, `player_two`, `winner_id`, `walkover`, `date`, `played_between`, `date_known_to`, `has_frames`, `status`, `record`.

MCP tool: `find_matches`.

Sample: `GET https://api.snookerapi.net/v1/matches?event_id=ev_80r2d1px&round=final`

```json
{
    "data": [
        {
            "id": "mt_fse5w2by",
            "event": {
                "id": "ev_80r2d1px",
                "name": "2026 World Snooker Championship",
                "season": "2025-26"
            },
            "stage": "main",
            "round": "final",
            "best_of": 35,
            "player_one": {
                "id": "pl_w2c64qam",
                "name": "Shaun Murphy",
                "score": 17
            },
            "player_two": {
                "id": "pl_ra6m6b4g",
                "name": "Wu Yize",
                "score": 18
            },
            "winner_id": "pl_ra6m6b4g",
            "walkover": false,
            "date": "2026-05-03",
            "played_between": {
                "from": "2026-05-03",
                "to": "2026-05-03"
            },
            "date_known_to": "day",
            "has_frames": true,
            "status": "finished",
            "record": {
                "first_recorded_at": null,
                "result_recorded_at": null,
                "last_changed_at": "2026-10-05T11:15:09Z",
                "changes": 0
            }
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 1,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 1,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null
    }
}
```

#### `GET /v1/matches/{match_id}`

One match, with its status, its frames where they are known (points and breaks of 50 and over), and every change to it since it entered our record.

Plans: every plan; the Free key reads yesterday onwards.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `match_id` | path | id | yes | The id, as the list calls return it. | `mt_fse5w2by` |

Errors: 401 unauthorized, 403 upgrade_required, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `event`, `stage`, `round`, `best_of`, `player_one`, `player_two`, `winner_id`, `walkover`, `date`, `played_between`, `date_known_to`, `has_frames`, `status`, `record`, `frames`, `history`.

MCP tool: `get_match`.

Sample: `GET https://api.snookerapi.net/v1/matches/mt_fse5w2by`

```json
{
    "data": {
        "id": "mt_fse5w2by",
        "event": {
            "id": "ev_80r2d1px",
            "name": "2026 World Snooker Championship",
            "season": "2025-26"
        },
        "stage": "main",
        "round": "final",
        "best_of": 35,
        "player_one": {
            "id": "pl_w2c64qam",
            "name": "Shaun Murphy",
            "score": 17
        },
        "player_two": {
            "id": "pl_ra6m6b4g",
            "name": "Wu Yize",
            "score": 18
        },
        "winner_id": "pl_ra6m6b4g",
        "walkover": false,
        "date": "2026-05-03",
        "played_between": {
            "from": "2026-05-03",
            "to": "2026-05-03"
        },
        "date_known_to": "day",
        "has_frames": true,
        "status": "finished",
        "record": {
            "first_recorded_at": null,
            "result_recorded_at": null,
            "last_changed_at": "2026-10-05T11:15:09Z",
            "changes": 0
        },
        "history": [],
        "frames": [
            {
                "number": 1,
                "points_one": 50,
                "points_two": 72,
                "breaks_one": [],
                "breaks_two": [
                    51
                ]
            },
            {
                "number": 2,
                "points_one": 50,
                "points_two": 60,
                "breaks_one": [
                    50
                ],
                "breaks_two": []
            },
            {
                "number": 3,
                "points_one": 53,
                "points_two": 86,
                "breaks_one": [],
                "breaks_two": [
                    61
                ]
            }
        ]
    },
    "meta": {
        "plan": "House key",
        "shown": "the first 3 frames of 35"
    }
}
```

#### `GET /v1/results/yesterday`

Every match finished yesterday (UTC): those our live tracker saw finish yesterday, and those whose own day is yesterday.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `event`, `stage`, `round`, `best_of`, `player_one`, `player_two`, `winner_id`, `walkover`, `date`, `played_between`, `date_known_to`, `has_frames`.

MCP tool: `yesterdays_results`.

Sample: `GET https://api.snookerapi.net/v1/results/yesterday`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27"
    }
}
```

### Players

Finding players and reading their records.

#### `GET /v1/players`

Players: everyone who has played in an event we hold, from 1927 on.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `q` | query | string | no | Words in the player's name. | `Sullivan` |
| `country` | query | string | no | Nationality, e.g. England, China. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `2` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `name`, `country`, `born`.

MCP tool: `find_players`.

Sample: `GET https://api.snookerapi.net/v1/players?q=Sullivan&per_page=2`

```json
{
    "data": [
        {
            "id": "pl_m1j79awf",
            "name": "Ronnie O'Sullivan",
            "country": "England",
            "born": "1975-12-05"
        },
        {
            "id": "pl_xcj1d100",
            "name": "Sean O'Sullivan",
            "country": "England",
            "born": "1994-04-29"
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 2,
        "next_cursor": "bzIuMjY0ZmE2",
        "page": 1,
        "per_page": 2,
        "total": 8,
        "total_pages": 4,
        "next_page": 2,
        "prev_page": null
    }
}
```

#### `GET /v1/players/{player_id}`

One player: nationality, birth date and place, the year they turned professional, their ranking, minor-ranking and world titles, the first season they were ranked, their highest world ranking, matches played and won, titles, and the seasons they span.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `player_id` | path | id | yes | The id, as the list calls return it. | `pl_ra6m6b4g` |

Errors: 401 unauthorized, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `name`, `country`, `born`, `birth_place`, `nickname`, `turned_pro`, `first_ranked_season`, `highest_ranking`, `ranking_titles`, `minor_ranking_titles`, `world_titles`, `form`, `latest_season`, `matches`, `wins`, `titles`, `first_season`, `last_season`, `century_breaks`, `maximum_breaks`.

MCP tool: `get_player`.

Sample: `GET https://api.snookerapi.net/v1/players/pl_ra6m6b4g`

```json
{
    "data": {
        "id": "pl_ra6m6b4g",
        "name": "Wu Yize",
        "country": "People's Republic of China",
        "born": "2003-10-14",
        "birth_place": "Lanzhou, Gansu, China",
        "nickname": null,
        "turned_pro": 2021,
        "first_ranked_season": "2021-22",
        "highest_ranking": {
            "position": 4,
            "season": "2025-26",
            "from": "ranking_lists"
        },
        "ranking_titles": 3,
        "minor_ranking_titles": null,
        "world_titles": 1,
        "form": "WWWWWLWWLW",
        "latest_season": {
            "season": "2026-27",
            "played": 20,
            "won": 12,
            "lost": 5,
            "frames_won": 80,
            "frames_lost": 67,
            "titles": 1
        },
        "matches": 257,
        "wins": 151,
        "titles": 3,
        "first_season": "2017-18",
        "last_season": "2026-27",
        "century_breaks": 166,
        "maximum_breaks": 1
    },
    "meta": {
        "plan": "House key"
    }
}
```

#### `GET /v1/head-to-head`

Two players' meetings: how many, who won, frames won by each, their finals, and every match between them, newest first.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `player_a` | query | id | yes | One player (pl_ id). | `pl_ra6m6b4g` |
| `player_b` | query | id | yes | The other player (pl_ id). | `pl_w2c64qam` |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `3` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `player_a`, `player_b`, `meetings`, `wins_a`, `wins_b`, `frames_a`, `frames_b`, `finals`, `last_meeting`, `matches`.

MCP tool: `head_to_head`.

Sample: `GET https://api.snookerapi.net/v1/head-to-head?player_a=pl_ra6m6b4g&player_b=pl_w2c64qam&per_page=3`

```json
{
    "data": {
        "player_a": {
            "id": "pl_ra6m6b4g",
            "name": "Wu Yize"
        },
        "player_b": {
            "id": "pl_w2c64qam",
            "name": "Shaun Murphy"
        },
        "meetings": 5,
        "wins_a": 4,
        "wins_b": 1,
        "frames_a": 39,
        "frames_b": 33,
        "finals": 1,
        "last_meeting": {
            "id": "mt_pzdj9mgg",
            "event": {
                "id": "ev_zr3zkbe3",
                "name": "2026 Shenzhen Open",
                "season": "2026-27"
            },
            "stage": "main",
            "round": "quarter_final",
            "best_of": 9,
            "player_one": {
                "id": "pl_w2c64qam",
                "name": "Shaun Murphy",
                "score": 4
            },
            "player_two": {
                "id": "pl_ra6m6b4g",
                "name": "Wu Yize",
                "score": 5
            },
            "winner_id": "pl_ra6m6b4g",
            "walkover": false,
            "date": "2026-10-02",
            "played_between": {
                "from": "2026-10-02",
                "to": "2026-10-02"
            },
            "date_known_to": "round",
            "has_frames": false,
            "status": "finished",
            "record": {
                "first_recorded_at": null,
                "result_recorded_at": null,
                "last_changed_at": "2026-10-05T10:51:54Z",
                "changes": 0
            }
        },
        "matches": [
            {
                "id": "mt_pzdj9mgg",
                "event": {
                    "id": "ev_zr3zkbe3",
                    "name": "2026 Shenzhen Open",
                    "season": "2026-27"
                },
                "stage": "main",
                "round": "quarter_final",
                "best_of": 9,
                "player_one": {
                    "id": "pl_w2c64qam",
                    "name": "Shaun Murphy",
                    "score": 4
                },
                "player_two": {
                    "id": "pl_ra6m6b4g",
                    "name": "Wu Yize",
                    "score": 5
                },
                "winner_id": "pl_ra6m6b4g",
                "walkover": false,
                "date": "2026-10-02",
                "played_between": {
                    "from": "2026-10-02",
                    "to": "2026-10-02"
                },
                "date_known_to": "round",
                "has_frames": false,
                "status": "finished",
                "record": {
                    "first_recorded_at": null,
                    "result_recorded_at": null,
                    "last_changed_at": "2026-10-05T10:51:54Z",
                    "changes": 0
                }
            },
            {
                "id": "mt_bvqnphbw",
                "event": {
                    "id": "ev_tbkgjcj9",
                    "name": "2026 Shanghai Masters",
                    "season": "2026-27"
                },
                "stage": "main",
                "round": "quarter_final",
                "best_of": 11,
                "player_one": {
                    "id": "pl_w2c64qam",
                    "name": "Shaun Murphy",
                    "score": 5
                },
                "player_two": {
                    "id": "pl_ra6m6b4g",
                    "name": "Wu Yize",
                    "score": 6
                },
                "winner_id": "pl_ra6m6b4g",
                "walkover": false,
                "date": null,
                "played_between": {
                    "from": "2026-07-27",
                    "to": "2026-08-02"
                },
                "date_known_to": "event",
                "has_frames": false,
                "status": "finished",
                "record": {
                    "first_recorded_at": null,
                    "result_recorded_at": null,
                    "last_changed_at": "2026-10-05T10:51:56Z",
                    "changes": 0
                }
            },
            {
                "id": "mt_fse5w2by",
                "event": {
                    "id": "ev_80r2d1px",
                    "name": "2026 World Snooker Championship",
                    "season": "2025-26"
                },
                "stage": "main",
                "round": "final",
                "best_of": 35,
                "player_one": {
                    "id": "pl_w2c64qam",
                    "name": "Shaun Murphy",
                    "score": 17
                },
                "player_two": {
                    "id": "pl_ra6m6b4g",
                    "name": "Wu Yize",
                    "score": 18
                },
                "winner_id": "pl_ra6m6b4g",
                "walkover": false,
                "date": "2026-05-03",
                "played_between": {
                    "from": "2026-05-03",
                    "to": "2026-05-03"
                },
                "date_known_to": "day",
                "has_frames": true,
                "status": "finished",
                "record": {
                    "first_recorded_at": null,
                    "result_recorded_at": null,
                    "last_changed_at": "2026-10-05T11:15:09Z",
                    "changes": 0
                }
            }
        ]
    },
    "meta": {
        "plan": "House key",
        "count": 3,
        "next_cursor": "bzMuYjc3MjM0",
        "page": 1,
        "per_page": 3,
        "total": 5,
        "total_pages": 2,
        "next_page": 2,
        "prev_page": null
    }
}
```

### Rankings and breaks

World ranking lists with points, century breaks and maximums.

#### `GET /v1/rankings`

World rankings: a season's list, position by position, with ranking points where they are known.

Plans: every plan; the Free key reads yesterday onwards.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `season` | query | season | no | Season, e.g. 2023-24. Left out: the newest season held. |  |
| `list` | query | string | no | Which list of the season, as meta.lists names them. Left out: the latest. |  |
| `player_id` | query | id | no | Only this player (pl_ id): their place in every list of the season. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `3` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 403 upgrade_required, 429 rate_limited, 429 quota_exceeded.

Returns: `season`, `list`, `position`, `player`, `points`.

MCP tool: `world_rankings`.

Sample: `GET https://api.snookerapi.net/v1/rankings?per_page=3`

```json
{
    "data": [
        {
            "season": "2025-26",
            "list": "revision 11",
            "position": 1,
            "player": {
                "id": "pl_ndtp08wj",
                "name": "Judd Trump"
            },
            "points": null
        },
        {
            "season": "2025-26",
            "list": "revision 11",
            "position": 2,
            "player": {
                "id": "pl_7r9jtk6a",
                "name": "Neil Robertson"
            },
            "points": null
        },
        {
            "season": "2025-26",
            "list": "revision 11",
            "position": 3,
            "player": {
                "id": "pl_vvv0gwc6",
                "name": "Zhao Xintong"
            },
            "points": null
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 3,
        "next_cursor": "bzMuYjc3MjM0",
        "page": 1,
        "per_page": 3,
        "total": 129,
        "total_pages": 43,
        "next_page": 2,
        "prev_page": null,
        "season": "2025-26",
        "list": "revision 11",
        "lists": [
            "revision 0",
            "revision 1",
            "revision 2",
            "revision 3",
            "revision 4",
            "revision 5",
            "revision 6",
            "revision 7",
            "revision 8",
            "revision 9",
            "revision 10",
            "revision 11"
        ]
    }
}
```

#### `GET /v1/centuries`

Century breaks: every break of 100 or more in the events we hold, by event, player or season.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id), highest first. | `ev_80r2d1px` |
| `player_id` | query | id | no | Only this player's centuries (pl_ id). |  |
| `season` | query | season | no | Season, e.g. 2025-26. |  |
| `stage` | query | string | no | Main stage or qualifying. One of: main, qualifying. |  |
| `min_points` | query | integer | no | Only breaks of at least this many points, e.g. 140. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `3` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `event`, `player`, `points`, `stage`.

MCP tool: `century_breaks`.

Sample: `GET https://api.snookerapi.net/v1/centuries?event_id=ev_80r2d1px&per_page=3`

```json
{
    "data": [
        {
            "event": {
                "id": "ev_80r2d1px",
                "name": "2026 World Snooker Championship",
                "season": "2025-26"
            },
            "player": {
                "id": "pl_p2pf44sw",
                "name": "Chang Bingyu"
            },
            "points": 147,
            "stage": "qualifying"
        },
        {
            "event": {
                "id": "ev_80r2d1px",
                "name": "2026 World Snooker Championship",
                "season": "2025-26"
            },
            "player": {
                "id": "pl_yevzgv8d",
                "name": "Oliver Sykes"
            },
            "points": 146,
            "stage": "qualifying"
        },
        {
            "event": {
                "id": "ev_80r2d1px",
                "name": "2026 World Snooker Championship",
                "season": "2025-26"
            },
            "player": {
                "id": "pl_zpmeb7ce",
                "name": "Mark Allen"
            },
            "points": 145,
            "stage": "main"
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 3,
        "next_cursor": "bzMuYjc3MjM0",
        "page": 1,
        "per_page": 3,
        "total": 254,
        "total_pages": 85,
        "next_page": 2,
        "prev_page": null
    }
}
```

#### `GET /v1/maximums`

Maximum breaks: every officially recognised 147, newest first, with the player, the opponent and the event.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `player_id` | query | id | no | Only this player's maximums (pl_ id). |  |
| `season` | query | season | no | Season, e.g. 2025-26. |  |
| `year` | query | year | no | Calendar year it was made in. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. | `2` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `date`, `season`, `player`, `opponent`, `event`, `match_id`.

MCP tool: `maximum_breaks`.

Sample: `GET https://api.snookerapi.net/v1/maximums?per_page=2`

```json
{
    "data": [
        {
            "date": "2026-07-28",
            "season": "2026-27",
            "player": {
                "id": "pl_2ykdqbh2",
                "name": "Xiao Guodong"
            },
            "opponent": {
                "id": "pl_7r9jtk6a",
                "name": "Neil Robertson"
            },
            "event": {
                "id": "ev_tbkgjcj9",
                "name": "2026 Shanghai Masters"
            },
            "match_id": "mt_vchgze3q"
        },
        {
            "date": "2026-07-24",
            "season": "2026-27",
            "player": {
                "id": "pl_gv596prf",
                "name": "Liam Highfield"
            },
            "opponent": {
                "id": "pl_n4chrd0v",
                "name": "Stephen Maguire"
            },
            "event": {
                "id": "ev_g79fmgz7",
                "name": "2026 British Open"
            },
            "match_id": "mt_jrysc9ep"
        }
    ],
    "meta": {
        "plan": "House key",
        "count": 2,
        "next_cursor": "bzIuMjY0ZmE2",
        "page": 1,
        "per_page": 2,
        "total": 246,
        "total_pages": 123,
        "next_page": 2,
        "prev_page": null
    }
}
```

### Bulk export

The whole record as files, rebuilt every day.

#### `GET /v1/export/{part}` (paid plans)

Bulk export: one part of the whole record as a gzip file of JSON lines, one record a line, in the same shape as the API, rebuilt daily.

Plans: Developer and Pro.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `part` | path | id | yes | The id, as the list calls return it. | `matches` |

Errors: 401 unauthorized, 403 upgrade_required, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `part`, `rows`, `built_at`.

Sample: `GET https://api.snookerapi.net/v1/export/matches`

```json
{
    "file": "snookerapi-matches-20261028.jsonl.gz",
    "content_type": "application/gzip",
    "parts": [
        "events",
        "matches",
        "frames",
        "players",
        "centuries",
        "rankings",
        "maximums",
        "prizes"
    ],
    "one_line": {
        "id": "mt_fse5w2by",
        "round": "final",
        "...": "..."
    }
}
```

### Your key

Your plan, calls and limits.

#### `GET /v1/usage`

Your key: its plan, calls today, limits, when they reset, and the data window the plan covers.

Plans: every plan.

Errors: 401 unauthorized, 429 rate_limited.

Returns: `plan`, `calls_today`, `per_day`, `per_minute`, `resets_at`, `window`.

Sample: `GET https://api.snookerapi.net/v1/usage`

```json
{
    "data": {
        "plan": "Free key",
        "calls_today": 0,
        "per_day": 50,
        "per_minute": 30,
        "resets_at": "2026-10-29T00:00:00Z",
        "window": {
            "from": "2026-10-27",
            "to": null
        }
    },
    "meta": {
        "plan": "Free key"
    }
}
```

### Live

Frame scores and events while matches are played: a feed, a stream and webhooks.

#### `GET /v1/matches/{match_id}/live`

One match as it stands: whether it is under way, the frames won, every frame known, and its latest change.

Plans: every plan; the Free key reads yesterday onwards.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `match_id` | path | id | yes | The id, as the list calls return it. | `mt_fse5w2by` |

Errors: 401 unauthorized, 403 upgrade_required, 404 not_found, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `status`, `best_of`, `player_one`, `player_two`, `frames_won`, `frames`, `winner_id`, `last_change`, `recent`.

MCP tool: `live_match`.

Sample: `GET https://api.snookerapi.net/v1/matches/mt_fse5w2by/live`

```json
{
    "data": {
        "id": "mt_fse5w2by",
        "event": {
            "id": "ev_80r2d1px",
            "name": "2026 World Snooker Championship",
            "season": "2025-26"
        },
        "stage": "main",
        "round": "final",
        "status": "finished",
        "best_of": 35,
        "player_one": {
            "id": "pl_w2c64qam",
            "name": "Shaun Murphy",
            "score": 17
        },
        "player_two": {
            "id": "pl_ra6m6b4g",
            "name": "Wu Yize",
            "score": 18
        },
        "frames_won": [
            17,
            18
        ],
        "frames": [
            {
                "number": 1,
                "points_one": 50,
                "points_two": 72,
                "breaks_one": [],
                "breaks_two": [
                    51
                ]
            },
            {
                "number": 2,
                "points_one": 50,
                "points_two": 60,
                "breaks_one": [
                    50
                ],
                "breaks_two": []
            },
            {
                "number": 3,
                "points_one": 53,
                "points_two": 86,
                "breaks_one": [],
                "breaks_two": [
                    61
                ]
            }
        ],
        "winner_id": "pl_ra6m6b4g",
        "last_change": "2026-10-05T11:15:09Z",
        "recent": []
    },
    "meta": {
        "plan": "House key",
        "seconds_behind": 2,
        "last_change": null,
        "poll_seconds": 300,
        "shown": "the first 3 frames of 35"
    }
}
```

#### `GET /v1/live/events`

The live feed: every score change, frame won, result and century in the events being played, oldest first after since.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `since` | query | id | no | The last le_ id you had: the feed carries on after it. Left out, the last hour. |  |
| `match_id` | query | id | no | Only this match (mt_ id). |  |
| `types` | query | string | no | Only these event types, comma separated: match.started, frame.won, match.finished, century.made. |  |
| `per_page` | query | integer | no | Events a call, up to 200. | `3` |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `type`, `at`, `event`, `match`, `data`.

Sample: `GET https://api.snookerapi.net/v1/live/events?per_page=3`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_since": "le_gakwevmj",
        "more": false,
        "seconds_behind": 2,
        "poll_seconds": 300
    }
}
```

#### `GET /v1/stream/token`

A token for the stream, for a browser: EventSource cannot send a header, so it opens /v1/stream?token=... instead of sending the key.

Plans: every plan.

Errors: 401 unauthorized, 429 rate_limited, 429 quota_exceeded.

Returns: `token`, `expires_at`, `url`.

Sample: `GET https://api.snookerapi.net/v1/stream/token`

```json
{
    "data": {
        "token": "st_MmdzZjVoZ3MuMTc5MzIyMDAwNC44MDk0ZGMxY2M1NWM0MjI4ZDVkM2E4YzNmMmY2OGFhYQ",
        "expires_at": "2026-10-28T20:40:04Z",
        "url": "https://api.snookerapi.net/v1/stream?token=st_MmdzZjVoZ3MuMTc5MzIyMDAwNC44MDk0ZGMxY2M1NWM0MjI4ZDVkM2E4YzNmMmY2OGFhYQ"
    },
    "meta": {
        "plan": "House key"
    }
}
```

#### `GET /v1/stream`

The live feed as Server-Sent Events, sent as it happens: one connection holds for five minutes, then reconnects by itself from the last event.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `match_id` | query | id | no | Only this match (mt_ id). |  |
| `types` | query | string | no | Only these event types, comma separated: match.started, frame.won, match.finished, century.made. |  |
| `since` | query | id | no | Replay from after this le_ id. EventSource sends Last-Event-ID by itself on reconnecting. |  |
| `token` | query | string | no | A token from GET /v1/stream/token, for a browser. Elsewhere send the key in a header as usual. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `type`, `at`, `event`, `match`, `data`.

Sample: `GET https://api.snookerapi.net/v1/stream?types=frame.won,match.finished`

```json
{
    "content_type": "text/event-stream",
    "one_event": "id: le_7qk3m2xa\nevent: frame.won\ndata: {\"id\":\"le_7qk3m2xa\",\"type\":\"frame.won\",\"at\":\"...\",\"event\":{...},\"match\":{...},\"data\":{\"score_one\":4,\"score_two\":3,\"best_of\":11,\"frames_won\":1}}"
}
```

### Yesterday's statistics



#### `GET /v1/stats/yesterday`

Yesterday (UTC) in one answer: matches, walkovers, frames, deciding frames, whitewashes, average match length, centuries and the highest break, in all and by stage.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `date`, `events`, `players`, `matches`, `walkovers`, `frames_played`, `deciding_frames`, `whitewashes`, `average_frames`, `centuries`, `highest_century`, `by_stage`.

MCP tool: `yesterdays_stats`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday`

```json
{
    "data": {
        "date": "2026-10-27",
        "events": 0,
        "players": 0,
        "matches": 0,
        "walkovers": 0,
        "frames_played": 0,
        "deciding_frames": 0,
        "whitewashes": 0,
        "average_frames": null,
        "centuries": 0,
        "highest_century": null,
        "by_stage": {}
    },
    "meta": {
        "plan": "House key",
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/players`

Every player who played yesterday: matches won and lost, frames won and lost, frame difference and frame win rate, centuries and their highest. Most wins first.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `player`, `nation`, `played`, `won`, `lost`, `drawn`, `frames_for`, `frames_against`, `frame_difference`, `frame_win_rate`, `centuries`, `highest_century`.

MCP tool: `yesterdays_player_stats`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/players`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/deciders`

Yesterday's matches settled in the deciding frame (won by one frame at full length), as the matches list gives them.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `event`, `stage`, `round`, `best_of`, `player_one`, `player_two`, `winner_id`, `date`, `date_known_to`, `has_frames`.

MCP tool: `yesterdays_deciders`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/deciders`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/whitewashes`

Yesterday's matches won without losing a frame, as the matches list gives them.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `id`, `event`, `stage`, `round`, `best_of`, `player_one`, `player_two`, `winner_id`, `date`, `date_known_to`, `has_frames`.

MCP tool: `yesterdays_whitewashes`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/whitewashes`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/upsets`

Yesterday's wins over a higher-ranked player, on the newest world ranking list, biggest ranking gap first. An unranked winner counts as one place below the list.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `match`, `upset_by`, `beaten`, `gap`, `ranking_list`.

MCP tool: `yesterdays_upsets`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/upsets`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none.",
        "ranking_list": {
            "season": "2025-26",
            "list": "revision 11"
        }
    }
}
```

#### `GET /v1/stats/yesterday/top16`

How the world's top 16 on the newest ranking list fared yesterday: each one who played, their matches, wins, losses and frames, and every result.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `position`, `player`, `played`, `won`, `lost`, `drawn`, `frames_for`, `frames_against`, `results`, `ranking_list`.

MCP tool: `yesterdays_top16`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/top16`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none.",
        "ranking_list": {
            "season": "2025-26",
            "list": "revision 11"
        },
        "top16_played": 0
    }
}
```

#### `GET /v1/stats/yesterday/nations`

Yesterday by nation, as the draws flag each player (ENG, CHN, WAL ...): players in action, matches won and lost, frames won and lost, centuries. Most wins first.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `nation`, `players`, `won`, `lost`, `drawn`, `frames_for`, `frames_against`, `centuries`.

MCP tool: `yesterdays_nations`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/nations`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/events`

Yesterday by event, and within each event by round: matches, frames, deciding frames, whitewashes and average match length; centuries and the highest by event.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `event`, `matches`, `walkovers`, `frames_played`, `deciding_frames`, `whitewashes`, `average_frames`, `centuries`, `highest_century`, `rounds`.

MCP tool: `yesterdays_events`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/events`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/scorelines`

How often each scoreline came up yesterday, by match length: 4-0, 4-1, 4-2, 4-3 in best of 7, and so on, with each one's share.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `best_of`, `scoreline`, `matches`, `share`.

MCP tool: `yesterdays_scorelines`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/scorelines`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

#### `GET /v1/stats/yesterday/centuries`

Every century break we had yesterday, highest first, with the player, the event and the stage.

Plans: every plan.

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `event_id` | query | id | no | Only this event (ev_ id). |  |
| `stage` | query | string | no | Only the main draw, qualifying or pre-qualifying. One of: main, qualifying, pre_qualifying. |  |
| `page` | query | integer | no | Page number, from 1. |  |
| `cursor` | query | string | no | The next_cursor from the previous page. |  |
| `per_page` | query | integer | no | Rows per page. |  |

Errors: 400 bad_request, 401 unauthorized, 422 unknown_parameter, 429 rate_limited, 429 quota_exceeded.

Returns: `player`, `points`, `event`, `stage`, `at`.

MCP tool: `yesterdays_centuries`.

Sample: `GET https://api.snookerapi.net/v1/stats/yesterday/centuries`

```json
{
    "data": [],
    "meta": {
        "plan": "House key",
        "count": 0,
        "next_cursor": null,
        "page": 1,
        "per_page": 50,
        "total": 0,
        "total_pages": 1,
        "next_page": null,
        "prev_page": null,
        "date": "2026-10-27",
        "note": "No match finished yesterday (2026-10-27, UTC). Days between events have none."
    }
}
```

## Guides

### Quick start

A key, the first call and the answer, in three steps. https://snookerapi.net/documentation/quick-start

1. Get a free key from your account at snookerapi.net. It starts `snk_live_` and is shown once: keep it somewhere safe.

2. Ask for something with the key in a header. The newest World Championship final:

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/matches?event_id=ev_80r2d1px&round=final"
```
3. Read the answer. Every answer is `data` (a record or a list) and `meta` (your plan, the counts and the next page). Ids such as `mt_fse5w2by` link records together: open the match with [GET /v1/matches/{match_id}](https://snookerapi.net/documentation/match) to see its frames.

Next: [Authentication](https://snookerapi.net/documentation/authentication), [Pagination](https://snookerapi.net/documentation/pagination) and [Errors](https://snookerapi.net/documentation/errors).

### Authentication

Sending the key, and what happens without one. https://snookerapi.net/documentation/authentication

Every call needs a key, sent in a header in one of two ways:

```
Authorization: Bearer snk_live_...\nX-API-Key: snk_live_...
```
A key in the address (`?key=`) is refused with 400 `bad_request`: addresses end up in logs and browser history, headers do not.

Without a key, or with one that is not valid, the answer is 401 `unauthorized`. Keys are made and replaced in your account; a replaced key keeps working for 24 hours so nothing breaks while you change it over.

The key you paste into these pages is kept in your browser only, as `snookerapi.net.api_key`, and filled into every page's Try it.

### Plans and limits

What each plan reads, its limits and the headers that report them. https://snookerapi.net/documentation/plans

The Free key: 30 calls a minute and 50 a day, reading yesterday, today and what is scheduled ahead on every endpoint. Real data, never samples.

The paid plans read the whole record from 1927 with higher limits. Prices are on [Pricing](https://snookerapi.net/pricing).

Every answer carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `X-Quota-Remaining`. Past the minute's limit the answer is 429 `rate_limited`, past the day's 429 `quota_exceeded`, each with `Retry-After` in seconds. [GET /v1/usage](https://snookerapi.net/documentation/usage) shows where your key stands and is not counted.

On every plan, matches more than 48 hours ahead are not returned.

### Pagination

Pages or a cursor, and the totals in meta. https://snookerapi.net/documentation/pagination

Lists return 50 rows a page by default and up to 200 with `per_page`. Two ways to move through them:

- `page=2`, `page=3`: `meta` then also gives `total`, `total_pages`, `next_page` and `prev_page`.
- `cursor`: pass `meta.next_cursor` from the last answer. Quicker on long lists, and steady while new rows arrive. It is null on the last page.

### Errors

Every error code, what it means and what to do. https://snookerapi.net/documentation/errors

An error is `{"error": {"code", "message", "docs"}}`, with `plan` and `pricing` added when a paid plan is needed. Match on `code`; the message is for people.

| Status | Code | What to do |
| --- | --- | --- |
| 400 | bad_request | A parameter has the wrong shape, or the key was sent in the address. The message names it. |
| 401 | unauthorized | Send a valid key in a header ([Authentication](https://snookerapi.net/documentation/authentication)). |
| 403 | upgrade_required | The record is in the archive the paid plans cover. Ask for something from yesterday on, or see Pricing. |
| 404 | not_found | No such address or id. Ids come from the list calls. |
| 405 | bad_request | Only GET (and OPTIONS) is answered. |
| 422 | unknown_parameter | The call does not take that parameter; the message lists the ones it does. |
| 429 | rate_limited, quota_exceeded | Wait for `Retry-After` seconds. |
| 500 | server_error | A fault on our side, already logged. Try again shortly. |

### Dates

Days, windows and how close a date is. https://snookerapi.net/documentation/dates

All dates and times are UTC. A match has `date` when the day it was played is known. Where it is not, `played_between` gives the window of days and `date_known_to` says how close that is:

- `day`: the day itself.
- `round`: the days its round was played.
- `stage`: the days of the main stage or of qualifying.
- `event`: the event's days.
- `month`: the month, for events dated only to a month.

Filter by days with `from` and `to` on [GET /v1/matches](https://snookerapi.net/documentation/matches): a match counts when its window touches them.

### Coverage

What the data holds and how far back. https://snookerapi.net/documentation/coverage

64,396 matches in 1,192 events from 1927, 3,130 players, 28,030 century breaks and world ranking lists over 49 seasons.

Frame-by-frame scores are held for 1,082 matches (13,873 frames), mostly finals: they exist where the frames were recorded. The World Championship final has them every year from 1982.

Tours: the professional tour in full, with the women's tour, the seniors' tour, Q School, the development tour (the Q Tour and the Challenge Tour before it, from 2018-19: each event's last rounds) and the amateur championships (IBSF world and junior, EBSA European, ACBS Asian) where they are recorded. Results are checked every five minutes while an event or its qualifying is being played.

### Events and draws

Tournaments, their draws, champions and prize money. https://snookerapi.net/documentation/events-and-draws

An event is one edition of a competition: the 2024 World Championship is an edition of the World Championship. Find events by season, year, tour or name with [GET /v1/events](https://snookerapi.net/documentation/events), then open one with [GET /v1/events/{event_id}](https://snookerapi.net/documentation/event) for its dates, venue, champion, final score, highest break and prize money.

Its draw is its matches: [GET /v1/matches](https://snookerapi.net/documentation/matches) with `event_id`, and `stage` or `round` to narrow it.

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/events?q=World+Snooker+Championship&per_page=2"
```

### Results and frames

Match results, rounds and frame-by-frame scores. https://snookerapi.net/documentation/results-and-frames

[GET /v1/matches](https://snookerapi.net/documentation/matches) lists results newest first, by event, player, season, stage, round or days. [GET /v1/results/yesterday](https://snookerapi.net/documentation/results-yesterday) is yesterday's in one call.

Where `has_frames` is true, [GET /v1/matches/{match_id}](https://snookerapi.net/documentation/match) gives every frame: both players' points and their breaks of 50 and over.

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/matches/mt_fse5w2by"
```

### Player records

Finding players and reading their records. https://snookerapi.net/documentation/player-records

[GET /v1/players](https://snookerapi.net/documentation/players) finds players by name (the most-played first) or nationality. [GET /v1/players/{player_id}](https://snookerapi.net/documentation/player) gives the record: matches, wins, titles, the seasons played, centuries and maximums.

Every match a player played: [GET /v1/matches](https://snookerapi.net/documentation/matches) with `player_id`.

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/players?q=Sullivan&per_page=2"
```

### World rankings

The ranking lists of each season. https://snookerapi.net/documentation/world-rankings

[GET /v1/rankings](https://snookerapi.net/documentation/rankings) gives a season's list position by position, with points where they were published. A season has several lists, revised through the year: `meta.lists` names them in order, and `list` picks one. With `player_id`, it gives that player's place in every list of the season.

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/rankings?per_page=3"
```

### Breaks

Century breaks and maximums. https://snookerapi.net/documentation/breaks

[GET /v1/centuries](https://snookerapi.net/documentation/centuries) lists the century breaks in the events we hold, by event (highest first), player, season, stage or size with `min_points`. [GET /v1/maximums](https://snookerapi.net/documentation/maximums) lists every officially recognised 147, newest first, with the opponent and the match where we hold it.

```
curl -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/centuries?event_id=ev_80r2d1px&per_page=3"
```

### Live scores

Following matches as they are played: one match, the feed, and the stream. https://snookerapi.net/documentation/live

While an event is being played, every change we take in becomes a live event: `match.started` (a score first appears), `frame.won` (the frames won go up), `match.finished` (a winner) and `century.made`. We look for changes every five minutes, so an event usually reaches you within a few minutes of it happening; every live answer says how fresh it is in `meta.seconds_behind` and `meta.poll_seconds`.

Three ways to follow them, on every plan:

- [GET /v1/matches/{match_id}/live](https://snookerapi.net/documentation/match-live): one match as it stands, its frames won and its latest events.
- [GET /v1/live/events](https://snookerapi.net/documentation/live-events): the feed itself. Keep `meta.next_since` and send it back as `since` to carry on where you were.
- [GET /v1/stream](https://snookerapi.net/documentation/stream): the same feed pushed to you as Server-Sent Events. A connection holds for five minutes, then reconnects by itself from the last event, so nothing is missed.

From a server, send the key in a header as usual:

```
curl -N -H "Authorization: Bearer $SNOOKER_KEY" "https://api.snookerapi.net/v1/stream?types=frame.won,match.finished"
```
In a browser, EventSource cannot send a header, so ask your server for a token with [GET /v1/stream/token](https://snookerapi.net/documentation/stream-token) and open the address it gives:

```
const es = new EventSource(url);   // https://api.snookerapi.net/v1/stream?token=st_...
es.addEventListener('frame.won', e => board.frame(JSON.parse(e.data)));
es.addEventListener('match.finished', e => board.final(JSON.parse(e.data)));
```
A key can hold three streams open at once. To have the events posted to your app instead, see [Webhooks](https://snookerapi.net/documentation/webhooks).

### Webhooks

The live events posted to your app, signed and retried. https://snookerapi.net/documentation/webhooks

On the paid plans, add up to ten endpoints in your account (Webhooks) and pick the events each one wants. Every live event of those types is then POSTed to it as JSON, in the same shape as [the live feed](https://snookerapi.net/documentation/live-events), with `test` false.

```
POST /hooks/snooker
Content-Type: application/json
X-Snooker-Event: frame.won
X-Snooker-Signature: t=1791874000,v1=5f2c9a...
```
Check every delivery before you trust it: the HMAC-SHA256 of `t`, a full stop and the raw body, with the endpoint's secret, must equal `v1`, and `t` should be within five minutes of now.

```
// PHP
[$t, $v1] = sscanf($_SERVER['HTTP_X_SNOOKER_SIGNATURE'], 't=%d,v1=%s');
$body = file_get_contents('php://input');
$ok = hash_equals(hash_hmac('sha256', $t . '.' . $body, $secret), $v1) && abs(time() - $t) < 300;

# Python
t, v1 = (p.split('=', 1)[1] for p in sig.split(','))
ok = hmac.compare_digest(hmac.new(secret.encode(), f'{t}.{body}'.encode(), 'sha256').hexdigest(), v1)
```
Answer with any 2xx within ten seconds. Anything else is tried again eight times over about three days (after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and twice a day later). A refusal that will not change by itself, such as 404 or 410, stops that delivery at once. After twenty failures in a row the endpoint is paused and you are emailed; Resume in your account starts it again.

Endpoints must be https and reachable on the public internet. Send a test from your account to see a real recent event arrive, marked `"test": true`.

### Changelog

What changed, newest first. https://snookerapi.net/documentation/changelog

- 28 Oct 2026Seeds: GET /v1/events/{event_id} lists the seeded players, as the event's main draw writes them.
- 28 Oct 2026GET /v1/events/{event_id}/points: the world ranking points each player earned at a ranking event, with whether our results give the same.
- 28 Oct 2026Fixtures: the matches of an event not yet finished are held as soon as its draw pairs the players, with status scheduled; GET /v1/matches?status=scheduled lists what is coming.
- 28 Oct 2026Matches dated to the day they were played from when our record had their result during the event, where the article gives only the round's days; each such date is in the match's history.
- 28 Oct 2026Every match has a status (scheduled, in_play, finished, drawn, walkover, incomplete, unrecorded) and a record of when it and its result reached us; GET /v1/matches takes status; GET /v1/matches/{match_id} lists every change; a match id taken out of the record answers 410 with its replacement.
- 28 Oct 2026Yesterday's statistics: ten calls under GET /v1/stats/yesterday (the day, players, deciders, whitewashes, upsets, top 16, nations, events, scorelines, centuries), on every key.
- 28 Oct 2026GET /v1/results/yesterday now counts every match our live tracker saw finish yesterday, not only those with a day of their own.
- 13 Oct 2026Live: GET /v1/matches/{match_id}/live, the live feed (GET /v1/live/events), the stream (GET /v1/stream, Server-Sent Events) and webhooks on the paid plans.
- 13 Oct 2026Match ids stay the same when an event is updated: a match is changed in place, never made again.
- 13 Oct 2026Rankings, centuries and maximums endpoints.
- 13 Oct 2026Frame scores for 59 World Championship finals, every one from 1982.
- 13 Oct 2026Championship League matches dated to the day their group was played.
- 12 Oct 2026Events, matches, players, yesterday's results and usage endpoints, with the free key.

### MCP

Connecting an assistant to the Snooker API. https://snookerapi.net/documentation/mcp

The MCP server puts the Snooker API into an assistant as tools: Claude, ChatGPT, Cursor or Claude Code call it with your key and answer snooker questions from the data. Its address is `https://snookerapi.net/mcp`.

Clients that take a header (Claude Code, Cursor and most desktop clients) connect now: the address, and your key as `Authorization: Bearer`. For Claude Code:

```
claude mcp add --transport http snookerapi https://snookerapi.net/mcp --header "Authorization: Bearer $SNOOKER_KEY"
```
Claude.ai and ChatGPT connectors sign in with OAuth instead of a header; that sign-in opens with the paid plans. Every call is a normal API call on your key, so its plan and limits apply. Every tool and the endpoint behind it is on [MCP tools](https://snookerapi.net/documentation/mcp-tools).

Example: asked "who won the 2024 world final, and what was his best break in it?", the assistant calls `find_events`, then `get_match`, and answers from the frames.

### MCP tools

Every tool, its purpose, parameters and endpoint. https://snookerapi.net/documentation/mcp-tools

| Tool | What it does | Calls | Parameters |
| --- | --- | --- | --- |
| find_events | Events: every tournament and championship, newest first. | [GET /v1/events](https://snookerapi.net/documentation/events) | season, year, tour, competition_id, q |
| get_event | One event: its dates, venue, champion, final, prize fund and its own figures (frames played, deciding frames, whitewashes, centuries). | [GET /v1/events/{event_id}](https://snookerapi.net/documentation/event) | none |
| event_ranking_points | The world ranking points each player earned at a ranking event, as the season's rankings publish them, and whether our own results for the event give the same points. | [GET /v1/events/{event_id}/points](https://snookerapi.net/documentation/event-points) | paging only |
| find_matches | Matches: results and draws, newest first, by event, player, season or dates. | [GET /v1/matches](https://snookerapi.net/documentation/matches) | event_id, player_id, season, stage, round, from, to, status |
| get_match | One match, with its status, its frames where they are known (points and breaks of 50 and over), and every change to it since it entered our record. | [GET /v1/matches/{match_id}](https://snookerapi.net/documentation/match) | none |
| yesterdays_results | Every match finished yesterday (UTC): those our live tracker saw finish yesterday, and those whose own day is yesterday. | [GET /v1/results/yesterday](https://snookerapi.net/documentation/results-yesterday) | paging only |
| find_players | Players: everyone who has played in an event we hold, from 1927 on. | [GET /v1/players](https://snookerapi.net/documentation/players) | q, country |
| get_player | One player: nationality, birth date and place, the year they turned professional, their ranking, minor-ranking and world titles, the first season they were ranked, their highest world ranking, matches played and won, titles, and the seasons they span. | [GET /v1/players/{player_id}](https://snookerapi.net/documentation/player) | none |
| head_to_head | Two players' meetings: how many, who won, frames won by each, their finals, and every match between them, newest first. | [GET /v1/head-to-head](https://snookerapi.net/documentation/head-to-head) | player_a, player_b |
| world_rankings | World rankings: a season's list, position by position, with ranking points where they are known. | [GET /v1/rankings](https://snookerapi.net/documentation/rankings) | season, list, player_id |
| century_breaks | Century breaks: every break of 100 or more in the events we hold, by event, player or season. | [GET /v1/centuries](https://snookerapi.net/documentation/centuries) | event_id, player_id, season, stage, min_points |
| maximum_breaks | Maximum breaks: every officially recognised 147, newest first, with the player, the opponent and the event. | [GET /v1/maximums](https://snookerapi.net/documentation/maximums) | player_id, season, year |
| live_match | One match as it stands: whether it is under way, the frames won, every frame known, and its latest change. | [GET /v1/matches/{match_id}/live](https://snookerapi.net/documentation/match-live) | none |
| yesterdays_stats | Yesterday (UTC) in one answer: matches, walkovers, frames, deciding frames, whitewashes, average match length, centuries and the highest break, in all and by stage. | [GET /v1/stats/yesterday](https://snookerapi.net/documentation/stats-yesterday) | event_id, stage |
| yesterdays_player_stats | Every player who played yesterday: matches won and lost, frames won and lost, frame difference and frame win rate, centuries and their highest. Most wins first. | [GET /v1/stats/yesterday/players](https://snookerapi.net/documentation/stats-yesterday-players) | event_id, stage |
| yesterdays_deciders | Yesterday's matches settled in the deciding frame (won by one frame at full length), as the matches list gives them. | [GET /v1/stats/yesterday/deciders](https://snookerapi.net/documentation/stats-yesterday-deciders) | event_id, stage |
| yesterdays_whitewashes | Yesterday's matches won without losing a frame, as the matches list gives them. | [GET /v1/stats/yesterday/whitewashes](https://snookerapi.net/documentation/stats-yesterday-whitewashes) | event_id, stage |
| yesterdays_upsets | Yesterday's wins over a higher-ranked player, on the newest world ranking list, biggest ranking gap first. An unranked winner counts as one place below the list. | [GET /v1/stats/yesterday/upsets](https://snookerapi.net/documentation/stats-yesterday-upsets) | event_id, stage |
| yesterdays_top16 | How the world's top 16 on the newest ranking list fared yesterday: each one who played, their matches, wins, losses and frames, and every result. | [GET /v1/stats/yesterday/top16](https://snookerapi.net/documentation/stats-yesterday-top16) | event_id, stage |
| yesterdays_nations | Yesterday by nation, as the draws flag each player (ENG, CHN, WAL ...): players in action, matches won and lost, frames won and lost, centuries. Most wins first. | [GET /v1/stats/yesterday/nations](https://snookerapi.net/documentation/stats-yesterday-nations) | event_id, stage |
| yesterdays_events | Yesterday by event, and within each event by round: matches, frames, deciding frames, whitewashes and average match length; centuries and the highest by event. | [GET /v1/stats/yesterday/events](https://snookerapi.net/documentation/stats-yesterday-events) | event_id, stage |
| yesterdays_scorelines | How often each scoreline came up yesterday, by match length: 4-0, 4-1, 4-2, 4-3 in best of 7, and so on, with each one's share. | [GET /v1/stats/yesterday/scorelines](https://snookerapi.net/documentation/stats-yesterday-scorelines) | event_id, stage |
| yesterdays_centuries | Every century break we had yesterday, highest first, with the player, the event and the stage. | [GET /v1/stats/yesterday/centuries](https://snookerapi.net/documentation/stats-yesterday-centuries) | event_id, stage |

### Agent skill

The one file that teaches an agent the Snooker API. https://snookerapi.net/documentation/agent-skill

The agent skill is one Markdown file with the calls, limits, errors and a workflow, written for an AI agent. Download [skill.md](https://snookerapi.net/skill.md) and save it as `~/.claude/skills/snookerapi/SKILL.md`, or upload it as a skill in Claude's settings. Agents also find it themselves at `/.well-known/agent-skills/index.json`.

The whole API in one file is the [manual](https://snookerapi.net/documentation/manual.md); [llms.txt](https://snookerapi.net/llms.txt) indexes both.

## Errors

An error is `{"error": {"code", "message", "docs"}}`, with `plan` and `pricing` added when a paid plan is needed. Match on `code`; the message is for people. Every code: https://snookerapi.net/documentation/errors

## About this document

Generated from the API's endpoint registry, its guides and its plan settings, never written by hand. The date at the top is the newest change to those sources: two copies with the same date are the same. For machines: the MCP server at https://snookerapi.net/mcp, the index at https://snookerapi.net/llms.txt, and the agent skill at https://snookerapi.net/skill.md.
