Skip to content

Player Report

Match report for one player: coverage, physical output, positioning, on-ball actions and interactions with other players.

Request

GET /analyses/{id}/player-report/{player_id}
Parameter Type Required Description
id integer Yes Analysis ID
player_id string Yes Player ID, e.g. 0-10 or 1-GK

404 when the player was never detected, when the id is not <team>-<jersey>, or when the team is not 0 or 1:

{ "detail": "No report data for player 0-7" }

Summary and Team Report list the players an analysis has.

The first request computes the report and caches it server-side; later requests are fast. The response is deterministic per (analysis_id, player_id, schema_version), which is a safe client-side cache key. Measured detection coverage and interactions.link_up need engine version 0.5.44 or later; older analyses return the fallbacks described per field.

Response

{
  "schema_version": 1,
  "engine_version": "0.5.44",
  "player_id": "0-10",
  "team": 0,
  "jersey": 10,
  "label": "10",
  "role": "player",
  "coordinate_frame": "attacking",
  "orientation_source": "game_meta",

  "params": {
    "sample_interval_ms": 40,
    "sprint_min_kmh": 25.2,
    "sprint_min_s": 1.0,
    "top_speed_percentile": 0.99,
    "reliable_seconds_floor": 600.0,
    "hull_central_fraction": 0.8,
    "max_sample_gap_ms": 400,
    "sequence_gap_ms": 5000,
    "sequence_shot_slack_ms": 1000,
    "pitch": { "length_m": 105.0, "width_m": 68.0 }
  },

  "coverage": {
    "seconds_detected": 4980.4,
    "seconds_projected": 512.0,
    "overlap_share": 0.081,
    "first_ms": 40,
    "last_ms": 5687240,
    "reliable": true,
    "source": "coverage_artifact"
  },

  "card": {
    "hero_crop_index": 0,
    "kit": {
      "primary": "red",
      "primary_rgb": [200, 16, 46],
      "secondary": "white",
      "secondary_rgb": [255, 255, 255],
      "pattern": "solid"
    }
  },

  "physical": {
    "source": "engine",
    "distance_m": 10542.5,
    "top_speed_kmh": 26.81,
    "speed_zones": [
      { "name": "walking",        "min_kmh": 0.0,  "max_kmh": 7.0,  "seconds": 2922.6, "distance_m": 3110.9 },
      { "name": "jogging",        "min_kmh": 7.0,  "max_kmh": 14.0, "seconds": 1425.3, "distance_m": 4038.0 },
      { "name": "running",        "min_kmh": 14.0, "max_kmh": 21.0, "seconds": 452.1,  "distance_m": 2172.8 },
      { "name": "high_intensity", "min_kmh": 21.0, "max_kmh": 25.2, "seconds": 110.3,  "distance_m": 704.8 },
      { "name": "sprinting",      "min_kmh": 25.2, "max_kmh": null, "seconds": 70.1,   "distance_m": 516.0 }
    ],
    "distance_per_bucket": [
      { "start_min": 0,  "end_min": 15,  "distance_m": 1822.1 },
      { "start_min": 15, "end_min": 30,  "distance_m": 1749.5 },
      { "start_min": 30, "end_min": 45,  "distance_m": 1669.4 },
      { "start_min": 45, "end_min": 60,  "distance_m": 1744.2 },
      { "start_min": 60, "end_min": 75,  "distance_m": 1693.4 },
      { "start_min": 75, "end_min": 90,  "distance_m": 1563.9 },
      { "start_min": 90, "end_min": 105, "distance_m": 300.0 }
    ],
    "sprints": [
      {
        "t0_ms": 1002280,
        "t1_ms": 1003320,
        "start": { "x": 62.8, "y": 10.3 },
        "end":   { "x": 71.3, "y": 12.2 },
        "peak_kmh": 29.2
      },
      {
        "t0_ms": 1353760,
        "t1_ms": 1355360,
        "start": { "x": 50.6, "y": 35.7 },
        "end":   { "x": 61.1, "y": 40.9 },
        "peak_kmh": 27.97
      }
      // ... 7 sprints in total
    ]
  },

  "position": {
    "heatmap": {
      "bins": [21, 14],
      "extent": [[0.0, 105.0], [0.0, 68.0]],
      "counts": [
        [0, 0, 4, 11, 26, 38, 51, 44, 30, 19, 8, 2, 0, 0],
        [0, 2, 9, 24, 47, 66, 88, 79, 55, 31, 14, 5, 1, 0]
        // ... 21 rows in total, one per x bin
      ]
    },
    "heatmap_with_ball": {
      "bins": [21, 14],
      "extent": [[0.0, 105.0], [0.0, 68.0]],
      "counts": [
        [0, 0, 1, 4, 9, 14, 18, 15, 11, 6, 2, 0, 0, 0]
        // ... 21 rows
      ]
    },
    "avg_position": [
      { "period": 1, "position": { "x": 58.4, "y": 39.6 }, "n_frames": 61240 },
      { "period": 2, "position": { "x": 61.9, "y": 41.6 }, "n_frames": 63270 }
    ],
    "zone_hull": [
      { "x": 34.1, "y": 22.2 },
      { "x": 41.8, "y": 16.4 },
      { "x": 72.6, "y": 19.9 },
      { "x": 80.3, "y": 44.7 }
      // ... one point per hull vertex, in order
    ],
    "third_timeline": [
      { "minute": 0, "third": "middle" },
      { "minute": 1, "third": "attacking" },
      { "minute": 2, "third": "middle" }
      // ... one entry per minute the player was detected in
    ],
    "ball_relative_heatmap": {
      "bins": [21, 14],
      "extent": [[-105.0, 105.0], [-68.0, 68.0]],
      "counts": [
        [0, 0, 0, 2, 15, 44, 91, 88, 39, 12, 3, 0, 0, 0]
        // ... 21 rows
      ]
    }
  },

  "on_ball": {
    "passes": [
      {
        "event_id": "06a0765b-1b30-7a1b-8000-1f7595b1f2f7",
        "t_ms": 1631160,
        "x": 44.8,
        "y": 9.7,
        "x_target": 61.2,
        "y_target": 24.5,
        "length_m": 22.1,
        "receiver_player_id": "0-9",
        "receiver_team": 0,
        "set_piece": "open_play",
        "pass_type": null,
        "body_part": "unknown"
      },
      {
        "event_id": "06a0765b-2431-7188-8000-e8224979b8ad",
        "t_ms": 1945200,
        "x": 80.3,
        "y": 62.1,
        "x_target": 92.7,
        "y_target": 38.4,
        "length_m": 26.8,
        "receiver_player_id": "0-11",
        "receiver_team": 0,
        "set_piece": "open_play",
        "pass_type": "cross",
        "body_part": "unknown"
      }
      // ... 41 passes in total
    ],
    "shots": [
      {
        "event_id": "06a0765b-5295-7d33-8000-b561079c5084",
        "t_ms": 5811200,
        "x": 92.4,
        "y": 31.8,
        "distance_to_goal_m": 12.8,
        "body_part": "unknown"
      }
    ],
    "carries": [
      {
        "event_id": "06a0765b-1b2c-7f1b-8000-cda285698ff6",
        "t_ms": 1641760,
        "x": 60.5,
        "y": 19.4,
        "x_target": 68.9,
        "y_target": 22.7,
        "length_m": 9.0
      }
      // ... 9 carries in total
    ],
    "tackles": [
      { "event_id": "06a0765b-3a11-7c04-8000-9a1e17f0b2c6", "t_ms": 2247080, "x": 38.2, "y": 23.0 }
    ],
    "blocks": [
      { "event_id": "06a0765b-4102-7bb9-8000-2d0c6b74a1e5", "t_ms": 3631760, "x": 26.4, "y": 30.1 }
    ],
    "network": [
      { "player_id": "0-9",  "n": 12 },
      { "player_id": "0-11", "n": 8 },
      { "player_id": "0-4",  "n": 5 }
      // ... one entry per identified receiver, 9 in total
    ],
    "touches": 215,
    "touch_grid": {
      "bins": [3, 3],
      "extent": [[0.0, 105.0], [0.0, 68.0]],
      "counts": [
        [7, 12, 9],
        [21, 46, 26],
        [18, 52, 22]
      ]
    },
    "involvement": {
      "n_spans": 215,
      "spans_per_min": 4.56,
      "spans": [
        { "t0_ms": 23240, "t1_ms": 23240 },
        { "t0_ms": 23680, "t1_ms": 23880 }
        // ... 215 spans in total
      ]
    },
    "best_sequence": {
      "spans": [
        { "player_id": "0-18", "t0_ms": 5794520, "t1_ms": 5794640 },
        { "player_id": "0-3",  "t0_ms": 5795440, "t1_ms": 5796280 },
        { "player_id": "0-10", "t0_ms": 5796680, "t1_ms": 5799760 }
        // ... one span per touch in the move, in time order
      ],
      "shot_event_id": "06a0765b-5295-7d33-8000-b561079c5084",
      "shot_t_ms": 5811200
    }
  },

  "interactions": {
    "pressure_received": {
      "mean_m": 4.25,
      "p10_m": 1.76,
      "n_frames": 3827,
      "by_third": [
        { "third": "defensive", "mean_m": 4.08, "n": 1177 },
        { "third": "middle",    "mean_m": 4.62, "n": 1716 },
        { "third": "attacking", "mean_m": 3.8,  "n": 934 }
      ]
    },
    "pressure_applied": {
      "n_possessions": 279,
      "heatmap": {
        "bins": [21, 14],
        "extent": [[0.0, 105.0], [0.0, 68.0]],
        "counts": [
          [0, 0, 0, 1, 6, 11, 14, 9, 4, 1, 0, 0, 0, 0]
          // ... 21 rows
        ]
      }
    },
    "direct_opponent": [
      { "player_id": "1-5", "share": 0.204 },
      { "player_id": "1-2", "share": 0.169 },
      { "player_id": "1-9", "share": 0.123 }
    ],
    "link_up": [
      { "player_id": "0-9",  "share": 0.181 },
      { "player_id": "0-4",  "share": 0.142 },
      { "player_id": "0-11", "share": 0.118 }
    ]
  }
}

Top level

Field Type Description
schema_version integer Version of this response shape. See Schema version
engine_version string Pipeline version the analysis was produced with
player_id string Requested player ID
team integer 0 or 1, the shirt-cluster label. Resolve to home/away via Teams & Sides
jersey int | null Shirt number. Null for non-numeric ids such as 0-GK
label string Id suffix, e.g. "10" or "GK"
role string goalkeeper or player
coordinate_frame string attacking, attacking_first_half or camera. See Coordinates
orientation_source string | null Where the attacking direction came from: hub (the video's left_start_team), game_meta (engine record) or tracking (inferred from the ends each team occupied). Null when none could resolve it; coordinate_frame is then camera

params

Thresholds the report was computed with. Read zone edges, bucket widths and the reliability floor from here rather than hardcoding them.

Field Type Unit Description
sample_interval_ms integer ms Time one detected sample represents (40 at 25 FPS)
sprint_min_kmh float km/h Speed a run must reach to count as a sprint
sprint_min_s float s Time that speed must be sustained
top_speed_percentile float Percentile of the speed distribution reported as top_speed_kmh
reliable_seconds_floor float s Detected time required for coverage.reliable
hull_central_fraction float Fraction of positions kept when building position.zone_hull
max_sample_gap_ms integer ms A larger gap between samples is a detection break, not play
sequence_gap_ms integer ms Maximum gap between two possessions in one on_ball.best_sequence
sequence_shot_slack_ms integer ms Maximum time a possession may close after a shot and still precede it in on_ball.best_sequence
pitch object m {length_m, width_m} of the pitch model

coverage

How much of the match the player was observed for. Every other section is computed over detected time only.

Field Type Unit Description
seconds_detected float s Time the player was seen on camera
seconds_projected float s Additional time the position was estimated while off camera. 0.0 when the analysis carries no projection
overlap_share float | null Fraction (0-1) of detected time where the player overlapped another player. Null when the analysis has no overlap information (tracking without a per-row status, before 0.5.36)
first_ms int | null ms First detection. Null when never seen
last_ms int | null ms Last detection. Null when never seen
reliable boolean seconds_detected >= params.reliable_seconds_floor. False means grey the stats out.
source string coverage_artifact (measured) or tracking_fallback (counted from detected tracking rows, on analyses without the coverage measurement)

Warning

Physical totals are sums over detected time. A substitute or a player far from the camera has a lower distance_m for that reason alone. Show seconds_detected next to any total.

card

Field Type Description
hero_crop_index int | null Index of a representative image, for GET /analyses/{id}/exemplar-crops/individuals/{player_id}/{hero_crop_index} (Exemplar Crops). Null when the analysis ships no crops
kit object | null Kit colours of the player's team. Null when the analysis carries no kit read

Kit object

Field Type Description
primary string Primary shirt colour name, from the same palette as Preview Teams
primary_rgb [int, int, int] | null [r, g, b], 0-255. Null when the name has no RGB mapping
secondary string | null Secondary colour name. Null on a single-colour kit
secondary_rgb [int, int, int] | null [r, g, b], 0-255
pattern string | null e.g. solid, stripes

physical

Distance and speed over detected samples only. Distance is sum(speed) * sample_interval, so a detection gap contributes nothing. Speed units and zone definitions are shared with Summary; see Speed & Distance for how the zones map onto the Summary buckets.

Field Type Unit Description
source string engine (pipeline frame_state speed) or tracking_fallback (insight's own smoother over tracking positions, on analyses without frame_state; treat as indicative). Independent of coverage.source
distance_m float m Total distance
top_speed_kmh float km/h params.top_speed_percentile percentile over every detected sample, standing still included. Lower than the fastest sprints[].peak_kmh. 0.0 when no speed data exists
speed_zones array Always 5 entries, low to high
distance_per_bucket array Distance per 15-minute bucket, contiguous from minute 0, gaps as 0.0. [] when no speed data exists
sprints array Runs at or above params.sprint_min_kmh for at least params.sprint_min_s. [] when none qualify

Speed zone object

Field Type Unit Description
name string walking, jogging, running, high_intensity, sprinting
min_kmh float km/h Lower bound, inclusive
max_kmh float | null km/h Upper bound, exclusive. Null on the last zone
seconds float s Time in the zone
distance_m float m Distance in the zone

The zones partition the speed series: distance_m over the five zones sums to physical.distance_m, and seconds sums to coverage.seconds_detected minus samples without a speed reading.

Fatigue bucket object

Field Type Unit Description
start_min integer min Bucket start
end_min integer min Bucket end
distance_m float m Distance inside the bucket

Sprint object

Field Type Unit Description
t0_ms integer ms Start
t1_ms integer ms End
start {x, y} | null m Position at start. Null when that frame has no position
end {x, y} | null m Position at end. Null when that frame has no position
peak_kmh float km/h Fastest speed in the run

position

Field Type Description
heatmap Grid Position over all detected frames. All-zero when there are none
heatmap_with_ball Grid | null Position over frames where the player's team had possession. Null when team possession is unavailable or never overlaps the player's frames
avg_position array Mean position per half. [] when there are no frames
zone_hull [{x, y}] Convex hull of the central params.hull_central_fraction of positions, as an ordered polygon in metres. [] or fewer than 3 points when there is too little data
third_timeline array Pitch third the player spent most of each minute in. Minutes without detections are omitted
ball_relative_heatmap Grid | null Player position minus ball position over frames where both are known, extent [[-105, 105], [-68, 68]]. Null when the analysis has no ball track

Average position object

Field Type Unit Description
period integer 1 or 2. A single period: 1 entry covers the whole match when coordinate_frame is attacking_first_half
position {x, y} m Mean position over the period
n_frames integer Frames behind the mean

Third timeline object

Field Type Description
minute integer Match minute, from 0
third string defensive, middle or attacking, relative to the player's attacking direction

on_ball

Actions where this player was the identified actor. Arrays are sorted by time and [] when nothing was detected. Every action row carries t_ms and the event_id of the underlying event.

Field Type Description
passes array Pass objects
shots array Shot objects
carries array Carry objects (drive events)
tackles array Point objects
blocks array Point objects
network array {player_id, n} pass counts per receiver, most first. [] when no receiver could be resolved
touches integer Times the player had the ball. Equal to involvement.n_spans
touch_grid Grid 3x3 count of touches. Sums to at most touches; a touch positioned outside the pitch is dropped
involvement object Touch rate and spans
best_sequence object | null Longest same-team move ending in a shot that includes this player. Null when there is none

Pass object

Field Type Unit Description
event_id string | null Id of the underlying event. Null on analyses whose event export predates event ids
t_ms integer ms Time of the pass
x, y float | null m Origin
x_target, y_target float | null m Destination. Null when not measured
length_m float | null m Null when the destination was not measured
receiver_player_id string | null Player that next had the ball. Null when not identified, including passes that went out of play
receiver_team int | null 0 or 1. Differs from the top-level team on a turnover. Null when not resolved
set_piece string | null Restart context, e.g. open_play, throw_in, corner_kick. See Pass
pass_type string | null e.g. cross. Null for a plain pass
body_part string | null e.g. head. unknown when not measured

Warning

receiver_player_id is an observation from the possession stream, not an adjudicated outcome. Do not derive a pass completion percentage from it.

Shot object

Field Type Unit Description
event_id string | null Id of the underlying event
t_ms integer ms Time of the shot
x, y float | null m Origin
distance_to_goal_m float | null m Straight-line distance to the centre of the attacked goal. Null when the shot has no position or when coordinate_frame is not attacking
body_part string | null See Shot

Carry object

Field Type Unit Description
event_id string | null Id of the underlying event
t_ms integer ms Start of the carry
x, y float | null m Start
x_target, y_target float | null m End. Null when not measured
length_m float | null m Null when the end was not measured

Point object (tackles, blocks)

Field Type Unit Description
event_id string | null Id of the underlying event
t_ms integer ms Time
x, y float | null m Position

Involvement object

Field Type Unit Description
n_spans integer Times the player had the ball
spans_per_min float | null Touches per minute of the player's team's possession. Null when team possession is unavailable
spans array {t0_ms, t1_ms} per touch, in time order, half-open [t0_ms, t1_ms). A single-frame touch has t0_ms == t1_ms

Best sequence object

Field Type Description
spans array {player_id, t0_ms, t1_ms} per touch in the move, in time order, teammates included
shot_event_id string | null Id of the shot that ended the move
shot_t_ms integer Time of the shot. spans[0].t0_ms to shot_t_ms is the clip window

interactions

Nearest-player relationships, computed per frame from the players visible on camera. Off-camera players cannot be nearest to anyone, so these numbers lean toward the camera's half of the pitch and are better compared within one match than across matches.

Field Type Description
pressure_received object | null Marking distance while on the ball. Null when there is no per-frame distance data or the player never had the ball
pressure_applied object | null Frames where the player was the closest defender to an opponent in possession. Null when there is no per-frame distance data
direct_opponent array Up to 3 {player_id, share}, opponents the player was nearest to most often. [] when there is no per-frame distance data
link_up array Up to 3 {player_id, share}, teammates the player was nearest to most often. [] before engine 0.5.44

share is a fraction (0-1) of frames. The entries are the top of a longer distribution and do not sum to 1.

Pressure received object

Field Type Unit Description
mean_m float m Mean distance to the nearest opponent over frames where the player had the ball
p10_m float m 10th percentile of that distance
n_frames integer Frames behind the numbers
by_third array Always 3 entries {third, mean_m, n} for defensive, middle, attacking. mean_m is null where n is 0

Pressure applied object

Field Type Description
n_possessions integer Opponent possessions during which this player was the ball holder's nearest opponent
heatmap Grid | null Position while applying that pressure. Null when no such frames exist

Coordinates

Coordinates are metres in x / y fields, points are {x, y}, polygons are lists of points, and span destinations are x_target / y_target. They are rotated so that the player's team attacks from x = 0 towards x = 105 in both halves. coordinate_frame states how far that rotation could be resolved:

Value Meaning
attacking Both halves oriented for the player's team
attacking_first_half The first-half direction applied to the whole match because the moment the teams changed ends could not be located. Second-half coordinates are mirrored
camera No attacking direction. Raw camera-frame coordinates as in Pitch Coordinates

Warning

Only attacking is safe to render as-is. In camera, thirds and distance_to_goal_m do not refer to the attacked goal. Query Intervals returns the camera frame by default; request coordinate_frame: "attacking" there to compare positions with this report. See Coordinate frames.

Time

Timestamps are milliseconds from match start in fields ending _ms. Divide by 1000 to align with the start / end seconds of Events and Query Intervals. event_id is the join key to the Events response.

Grid object

Every heatmap (position.*, on_ball.touch_grid, interactions.pressure_applied.heatmap) has this shape.

Field Type Description
bins [int, int] [nx, ny]
extent [[float, float], [float, float]] [[x_min, x_max], [y_min, y_max]] in metres
counts int[nx][ny] Frame counts. Row index is the x bin, column index the y bin, low first

Bin width is (x_max - x_min) / nx; height is (y_max - y_min) / ny. Multiply a count by params.sample_interval_ms for dwell time.

Schema version

schema_version covers the response shape and the thresholds in params; it is bumped when a field is added, removed or renamed, or a threshold changes. Currently 1. Key caches on (analysis_id, player_id, schema_version).

Warning

Actions are attributed only when the actor was identified, so passes, shots, carries, tackles and blocks are lower bounds. Present them as detected counts alongside coverage.