Get match timeline
Returns a played match’s events in order: goals, shots, cards, fouls, offsides, corners, substitutions, penalties, VAR reviews and period markers, each with its team and player where known. Stoppage time is kept separate, so 45+3’ comes back as "minute": 45, "extra_time": 3.
A corner_kick event carries the team and player that conceded the corner, so the team taking it is the other side. /stats corner_kicks counts corners for the team that took them, so per-team corner counts built from the timeline come out the other way round.
Goal events (goal, penalty_scored, own_goal) are the goals that stood. A goal disallowed after a VAR review is left out, and the review stays in as a var event with var_decision: "goal_disallowed". Each var event has a var_decision saying what the review decided. If the goal events ever disagree with the match score, trust the score.
meta.coverage says which event types the timeline can contain. full is every event type. partial is goals, penalties scored, own goals, cards and substitutions only, with no shots, fouls, corners, offsides, VAR reviews, missed penalties, shoot-out kicks or period markers. Neither guarantees that every event in the match is present: a full timeline can occasionally miss events or period markers.
To check that a finished match’s timeline is complete: the match status from GET /football/matches/{match_id} is finished, there is a period_end for the last period played (second_half, or extra_time_second_half after extra time), and the goal events add up to the score (score.regulation, or score.after_extra_time when the match went to extra time; shoot-out kicks don’t count).
When there’s no timeline you get 200 with an empty events list, meta.coverage = "none" and a reason: match_not_started, match_not_played (postponed or cancelled), no_events or not_supported (the competition isn’t covered). coverage: none means the data is unavailable, not that nothing happened in the match. 404 means the match doesn’t exist. While the match is live it returns 409; use /live-timeline instead.
Example: GET /football/matches/mt_838955483/timeline?event_type=goal,penalty_scored,yellow_card
curl --request GET \
--url https://api.thestatsapi.com/api/football/matches/{match_id}/timeline \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.thestatsapi.com/api/football/matches/{match_id}/timeline', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.thestatsapi.com/api/football/matches/{match_id}/timeline"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": {
"match_id": "mt_838955483",
"coverage": "full",
"events": [
{
"sequence": 3,
"minute": 45,
"extra_time": 3,
"period": "first_half",
"type": "yellow_card",
"team": {
"id": "tm_1002",
"name": "Aston Villa",
"slug": "aston-villa-tm_1002"
},
"player": {
"id": "pl_61849099",
"name": "Ollie Watkins",
"slug": "ollie-watkins-pl_61849099"
},
"var_decision": "goal_disallowed"
}
]
},
"meta": {
"total": 10,
"coverage": "full",
"last_updated": "2026-05-15T21:05:49.659Z"
}
}Authorizations
Your API key, sent as Authorization: Bearer YOUR_API_KEY. Get one at https://www.thestatsapi.com/api-keys
Path Parameters
Query Parameters
Comma-separated event types to keep (values of type, e.g. goal, yellow_card, substitution). Defaults to all. Unknown values return 400. Penalty goals are penalty_scored and own goals are own_goal, not goal, so use goal,penalty_scored,own_goal to get every goal; shoot-out kicks are also penalty_scored with period penalties
curl --request GET \
--url https://api.thestatsapi.com/api/football/matches/{match_id}/timeline \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.thestatsapi.com/api/football/matches/{match_id}/timeline', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.thestatsapi.com/api/football/matches/{match_id}/timeline"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text){
"data": {
"match_id": "mt_838955483",
"coverage": "full",
"events": [
{
"sequence": 3,
"minute": 45,
"extra_time": 3,
"period": "first_half",
"type": "yellow_card",
"team": {
"id": "tm_1002",
"name": "Aston Villa",
"slug": "aston-villa-tm_1002"
},
"player": {
"id": "pl_61849099",
"name": "Ollie Watkins",
"slug": "ollie-watkins-pl_61849099"
},
"var_decision": "goal_disallowed"
}
]
},
"meta": {
"total": 10,
"coverage": "full",
"last_updated": "2026-05-15T21:05:49.659Z"
}
}