Get match shot map
Get every shot in a match with its pitch position, outcome and expected goals (xG). Use it to draw a shot map, compare chance quality between teams or break down one player's shooting. Pass player_id to get only that player's shots.
https://api.soccer-data.co/v1/matches/{match_id}/shotmap
Live
- Check shotmap_available on the match first. It is only true when at least one shot has an xG above zero.
- It also works during a match. meta.is_final is false while the match is live, so more shots can still appear, and an empty data list just means nobody has shot yet.
- A shot's expected_goals can be null when no xG exists for it. The match's xg_quality tells you what to expect.
- Penalties are left out of np_xg_summary. stored can differ from live when a match-level figure has been recorded.
- Returns 404 STATS_NOT_AVAILABLE when detailed stats were never collected for the match, and 404 NOT_FOUND when the match is unknown or has no shot data.
The match's id, like match_…. Find one with the ID guide.
Only return shots taken by this player.
The match's id.
The match's id again.
Home team's id. Compare it with each shot's team_id to tell the sides apart.
Away team's id.
Every shot in the match, or only the chosen player's shots.
The shot's id.
Id of the player who took the shot.
Name of the player who took the shot.
Id of the shooting team.
Name of the shooting team.
Distance from the goal being attacked, from 0 to 100. 0 is that goal line and 100 is the shooter's own goal line. Every shot is flipped to attack the same goal, so you can plot both teams on one half. The edge of the box is at 16.5 and the penalty spot at about 11.
Position across the pitch, from 0 to 100. 50 is the middle of the goal. The penalty box runs from 20.5 to 79.5.
Minute the shot was taken.
What happened to the shot, for example goal, save, miss, block, blocked_off_line or post.
The shot's xG: how likely a shot like this is to go in. Null when no xG exists for it.
How the chance came about: assisted, regular or fast_break (open play), corner, free_kick, set_piece or throw_in_set_piece (dead ball), penalty, shootout or own_goal.
What the shot was taken with, for example right_foot.
Only set for goals, for example regular or own. Null otherwise.
Which part of the goal mouth the shot went to, for example low_centre.
Where the shot reached the goal mouth, as x, y and z numbers.
Where the shot was blocked, as x, y and z numbers.
Whether the shot was blocked.
Id of the player who blocked it.
The goalkeeper facing the shot, with id and name.
Whether the shot was a goal.
True for goals, saves and shots cleared off the line. False for own goals, which show up with goal_type own.
Whether the shot was a header.
True when the shot was taken outside the penalty box (x above 16.5, or y outside 20.5 to 79.5).
Whether the shot was a penalty.
Non-penalty xG per team, added up from the shots in data. Has home_team and away_team numbers.
The recorded match-level non-penalty xG, with home_team and away_team. Same as live when no recorded figure exists.
False while the match is live and shots can still be added. True otherwise.
curl "https://api.soccer-data.co/v1/matches/match_9pd0ck6g2m9i/shotmap" \ -H "X-Api-Key: $SOCCER_DATA_KEY"
const res = await fetch("https://api.soccer-data.co/v1/matches/match_9pd0ck6g2m9i/shotmap", {
headers: { "X-Api-Key": process.env.SOCCER_DATA_KEY },
});
const { data } = await res.json();
import os, requests
res = requests.get(
"https://api.soccer-data.co/v1/matches/match_9pd0ck6g2m9i/shotmap",
headers={"X-Api-Key": os.environ["SOCCER_DATA_KEY"]},
)
data = res.json()["data"]
$data = Http::withHeaders(['X-Api-Key' => env('SOCCER_DATA_KEY')])
->get('https://api.soccer-data.co/v1/matches/match_9pd0ck6g2m9i/shotmap')
->json('data');
{
"match_id": "match_9pd0ck6g2m9i",
"event": {
"id": "match_9pd0ck6g2m9i",
"home_team_id": "team_4mimn5fmya2v",
"away_team_id": "team_74p8tg2k93z4"
},
"data": [
{
"id": "shot_yb504px5a0rx",
"player_id": "player_85xcr2xm5vxo",
"player_name": "Ollie Watkins",
"team_id": "team_4mimn5fmya2v",
"team_name": "Aston Villa",
"x": 16.3,
"y": 45.8,
"minute": 2,
"result": "save",
"expected_goals": 0.118,
"situation": "fast_break",
"body_part": "right_foot",
"goal_type": null,
"goal_mouth_location": "low_centre",
"goal_mouth_coordinates": {
"x": 0,
"y": 50.1,
"z": 5.7
},
"block_coordinates": {
"x": 1.9,
"y": 50.2,
"z": 0
},
"is_blocked_shot": false,
"blocked_by_player_id": null,
"goalkeeper": {
"id": "player_nyb14jgqd9cz",
"name": "Giorgi Mamardashvili"
},
"is_goal": false,
"is_on_target": true,
"is_headed": false,
"is_outside_box": false,
"is_penalty": false
}
],
"np_xg_summary": {
"live": {
"home_team": 2.397,
"away_team": 1.572
},
"stored": {
"home_team": 2.397,
"away_team": 1.572
}
},
"meta": {
"is_final": true
}
}
### GET /matches/{match_id}/shotmap
Get match shot map
Get every shot in a match with its pitch position, outcome and expected goals (xG). Use it to draw a shot map, compare chance quality between teams or break down one player's shooting. Pass player_id to get only that player's shots.
- Check shotmap_available on the match first. It is only true when at least one shot has an xG above zero.
- It also works during a match. meta.is_final is false while the match is live, so more shots can still appear, and an empty data list just means nobody has shot yet.
- A shot's expected_goals can be null when no xG exists for it. The match's xg_quality tells you what to expect.
- Penalties are left out of np_xg_summary. stored can differ from live when a match-level figure has been recorded.
- Returns 404 STATS_NOT_AVAILABLE when detailed stats were never collected for the match, and 404 NOT_FOUND when the match is unknown or has no shot data.
Query parameters:
- `player_id` (string): Only return shots taken by this player.
Response fields:
- `match_id` (string): The match's id.
- `event.id` (string): The match's id again.
- `event.home_team_id` (string): Home team's id. Compare it with each shot's team_id to tell the sides apart.
- `event.away_team_id` (string): Away team's id.
- `data[]` (array): Every shot in the match, or only the chosen player's shots.
- `data[].id` (string): The shot's id.
- `data[].player_id` (string): Id of the player who took the shot.
- `data[].player_name` (string): Name of the player who took the shot.
- `data[].team_id` (string): Id of the shooting team.
- `data[].team_name` (string): Name of the shooting team.
- `data[].x` (number): Distance from the goal being attacked, from 0 to 100. 0 is that goal line and 100 is the shooter's own goal line. Every shot is flipped to attack the same goal, so you can plot both teams on one half. The edge of the box is at 16.5 and the penalty spot at about 11.
- `data[].y` (number): Position across the pitch, from 0 to 100. 50 is the middle of the goal. The penalty box runs from 20.5 to 79.5.
- `data[].minute` (integer): Minute the shot was taken.
- `data[].result` (string): What happened to the shot, for example goal, save, miss, block, blocked_off_line or post.
- `data[].expected_goals` (number|null): The shot's xG: how likely a shot like this is to go in. Null when no xG exists for it.
- `data[].situation` (string|null): How the chance came about: assisted, regular or fast_break (open play), corner, free_kick, set_piece or throw_in_set_piece (dead ball), penalty, shootout or own_goal.
- `data[].body_part` (string|null): What the shot was taken with, for example right_foot.
- `data[].goal_type` (string|null): Only set for goals, for example regular or own. Null otherwise.
- `data[].goal_mouth_location` (string|null): Which part of the goal mouth the shot went to, for example low_centre.
- `data[].goal_mouth_coordinates` (object|null): Where the shot reached the goal mouth, as x, y and z numbers.
- `data[].block_coordinates` (object|null): Where the shot was blocked, as x, y and z numbers.
- `data[].is_blocked_shot` (boolean): Whether the shot was blocked.
- `data[].blocked_by_player_id` (string|null): Id of the player who blocked it.
- `data[].goalkeeper` (object|null): The goalkeeper facing the shot, with id and name.
- `data[].is_goal` (boolean): Whether the shot was a goal.
- `data[].is_on_target` (boolean): True for goals, saves and shots cleared off the line. False for own goals, which show up with goal_type own.
- `data[].is_headed` (boolean): Whether the shot was a header.
- `data[].is_outside_box` (boolean): True when the shot was taken outside the penalty box (x above 16.5, or y outside 20.5 to 79.5).
- `data[].is_penalty` (boolean): Whether the shot was a penalty.
- `np_xg_summary.live` (object): Non-penalty xG per team, added up from the shots in data. Has home_team and away_team numbers.
- `np_xg_summary.stored` (object): The recorded match-level non-penalty xG, with home_team and away_team. Same as live when no recorded figure exists.
- `meta.is_final` (boolean): False while the match is live and shots can still be added. True otherwise.
Example: curl "https://api.soccer-data.co/v1/matches/match_9pd0ck6g2m9i/shotmap" -H "X-Api-Key: $SOCCER_DATA_KEY"