Skip to content

Changelog

Notable changes to API v1, newest first.

[1.26.0] - 2026-10

Faster running and top speed read closer to the truth.

Changed

  • Speeds come from positions that keep more of each acceleration, so top speed reads about 5% low instead of 10%, and distance at 21 km/h and above is no longer about a quarter low.
  • Every estimate (distance_m_est, the distance_bands estimates, sprints_est, sprint_distance_m_est, the Summary total_distance_m_est, the team report total) projects the untracked minutes at each speed band's own pace instead of the tracked pace, because a player is tracked most while play is near him. Broadcast jogging no longer reads about a quarter high.
  • Sprint estimate factors re-fitted and set per footage: params.sprint_m_per_high_speed_m 0.52 to 0.39 (broadcast) / 0.34 (non-broadcast), params.sprints_per_high_speed_m 0.026 to 0.019 / 0.017. Analyses without a footage class use the broadcast values.
  • ball_out_of_play_sec (Summary, Team Report) counts only dead-ball time inside the halves; half-time and pre-match footage no longer count. Typical error 6% to 4% on broadcast, 27% to 19% on non-broadcast.
  • Analyses pick up the new speeds when re-aggregated; the new estimates apply to every analysis on its next report build.

[1.25.0] - 2026-10

Covered minutes stop at the final whistle.

Changed

  • minutes_covered, and every estimate projected over it (distance_m_est, the distance_bands estimates, sprints_est, sprint_distance_m_est), counts only time inside the two halves. On non-broadcast footage it read about a fifth high from pre-match, half-time and post-match footage; now about 5%. Estimated distance there: typical error 11% to 8%.
  • Match possession pct (Summary, Team Report) is calibrated toward 50/50: the raw split ran about 2 points wide on the side ahead. Typical error on ground truth 2.6 to 1.4 points on broadcast, 3.2 to 1.8 on non-broadcast. seconds and per_minute are unchanged.
  • Team Report territory counts in-play time only and is calibrated per third: it read the defensive third about 10 points high and the attacking third 10 low. Typical error on broadcast ground truth 10 to about 1 point per third.
  • Sprint estimate factors re-fitted on the new window: params.sprint_m_per_high_speed_m 0.47 to 0.52, params.sprints_per_high_speed_m 0.023 to 0.026.

Added

  • Player Report coverage.seconds_in_halves: detected plus projected seconds inside the halves. Null when the analysis has no detected halves; minutes_covered then keeps its old meaning.

[1.24.0] - 2026-10

Sprints and covered minutes on the Summary, so a squad compares from one call.

Added

  • Summary players[].stats gains minutes_covered, sprint_count, sprint_distance_m, sprints_est and sprint_distance_m_est. Each is the same number as the Player Report field of the same name.

Unchanged

  • distance_by_speed_bucket.sprinting_m is still the distance at 21 km/h and above, not sprint distance.

[1.23.0] - 2026-10

Sprint estimates on the same footing as the distance estimate, and published accuracy per footage type.

Added

  • Player Report physical.sprints_est and physical.sprint_distance_m_est: sprints over the player's covered minutes, estimated from the high-speed distance. Measured sprints count about a fifth of the real ones; the estimates are right on average on broadcast footage (about 30% per player). Factors in params.
  • An accuracy table for every physical number, broadcast and non-broadcast.

[1.22.0] - 2026-10

Sprint distance that matches the sprint count.

Added

  • Player Report physical.sprint_distance_m and sprints[].distance_m: the metres covered inside counted sprints. 0.0 when a player has no sprints.
  • params.sprint_exit_kmh (19.8): a sprint now ends when the speed drops below it, not at the first stride under 25.2 km/h.

Changed

  • A sprint whose speed dips briefly under 25.2 km/h stays one sprint, so players get more sprints and fewer sub-second pieces that never counted.

[1.21.3] - 2026-10

Player pixel coordinates stay on the event's player.

Changed

  • pixel_coords follow the player only while the tracking around the event is continuous. They stop where the player leaves the view or the tracking jumps to another person, so t0 can be later than the interval start.
  • An event whose player is unknown, or not tracked at the event, returns pixel_coords that hold the player's position at the event for 1 s. It returned null.

[1.21.2] - 2026-10

Interval fixes on Query Intervals and a team id fix on the oldest analyses.

Fixed

  • ball_action and event_property rows on analyses from the first engine releases carry team_id as team_0 or team_1. They returned team_0.0 and team_1.0.
  • A row that starts after the video end returns a zero-length interval at the video end. It returned an end before its start.
  • Rows with the same start and end come back in a fixed order: by type, label, player_id, then team_id. The order varied between calls.

[1.21.1] - 2026-10

Numbers in the event property in constraint.

Changed

  • in on an event_property constraint accepts numbers and booleans, e.g. {"field": "team_label", "in": [0, 1]}. A list that mixes types returns 422.

[1.21.0] - 2026-10

The JSON tracking download is gone.

Removed

  • format=json on Download Tracking returns 410. Use format=clean to draw the match and format=parquet to analyse it.

[1.20.0] - 2026-10

Event properties on the event list.

Added

  • include=properties on Events: every row gains properties, the values of each property Schema lists for its type, and event_status.

Unchanged

  • GET /analyses/{id}/events without include returns the same rows and fields as before.

[1.19.0] - 2026-10

Three filters for possession changes, zone entries and progressive passes.

Added

  • possession_change: the moments a team won or lost the ball, by open play or restart and by third of the pitch.
  • zone_entry: passes and carries into the final third or the opponent penalty box.
  • progressive_pass: passes that moved the ball toward goal, by the progressive carry rule.

[1.18.1] - 2026-10

Docs correction. The API is unchanged; these pages now describe what it returns.

Fixed

  • Times in Events, Query Intervals, the reports and Download Tracking count from video start, not match start.
  • Rate limits are per account on POST /videos (5 per minute, 200 per day) and POST /videos/{id}/analysis (10 per 5 minutes, 300 per week). Other endpoints are not rate limited.
  • Errors raised by the hub carry message, including 401, 403, 404 and the 504 proxy timeout. Errors raised by insight carry detail. A result requested before the analysis is done returns 503, not 400.
  • match_timeline emits first_half, second_half and match when the engine locates the halves, not only full_game.
  • pass and carry are spans with end after start, not point events.
  • team_label on goal and shot is the scoring and shooting team, and team_id follows it.
  • Report examples show schema_version 5 and params.est_seconds_floor.
  • The tracking parquet has a team column, and x / y can be null. format=json can time out with 504.
  • analysis_id in webhooks is an integer. Goalkeepers are always {team}-GK.
  • Videos carry no analyses list and analyses no created_at. DELETE /videos/{id} returns 409 while an analysis on the video runs.

[1.18.0] - 2026-10

Every team-level row carries team_id, so a client never has to match a team by its display name. Schema marks experimental event types and properties an analysis never fills.

Added

  • team_id on every Events row: the actor's team on player events, the credited team on team_possession and goal, null otherwise. label is unchanged.
  • team_possession and player_possession rows from Query Intervals carry player_id, team_id and team_name, the same shape as the ball_action rows.
  • status per event type on Schema: experimental when the analysis's quality record for that type is flagged unreliable, else stable.
  • available per property on Schema: false when the analysis never fills it (null or unknown on every event of the type). The event pages mark pass_result on pass and body_part on shot as not currently populated.

Changed

  • Distance ignores tracking jumps above 10 m/s, the rule top speed already used. Clean matches move by under 1 %.
  • A player's full-game distance estimate, per-minute rate and average speed are null when they are physically implausible (above 150 m/min, or more than 30 % sprinting), and a keeper's estimate is null because keepers are mostly seen near play. physical_withheld gives the reason; measured distance and top speed stay. See Speed & distance.
  • Report schema_version is now 5.

[1.17.0] - 2026-09

Possession is a share of in-play time, and the two teams sum to 100. Goals and team possessions from the Event Property filter carry their team.

Changed

  • possession on the Summary and the Team Report splits only in-play time credited to a team. On one match 35 / 39 / 26 unknown becomes 47 / 53.
  • The unknown bucket, the unknown_pct per-minute field and the Team Report team: null row are removed. ball_out_of_play_sec and unattributed_in_play_sec report the time left out. See Possession.
  • Report schema_version is now 4. Refresh caches keyed on it.
  • The team_possession events are unchanged.
  • teams on the Event Property filter now matches events without a player (goal, team_possession) by team_label. It returned nothing for them. Goals with an unknown team are excluded when teams is set.
  • Those rows now carry team_id and team_name, with player_id null. A goal whose team the engine could not tell has both null.

[1.16.0] - 2026-09

A drive event marks the carries of at least 3 m, with progressive and take-on flags. drive no longer means carry.

Added

  • The drive event: a carry of at least 3 m, with the carry's span, actor and properties plus progressive and take_on. Returned by Events, Query Intervals and the Summary event histograms, and queryable with the Event Property filter. Available on analyses from engine version 0.5.45.
  • on_ball.drives on the Player Report, and params.drive_min_length_m and params.take_on_max_opp_dist_m on both reports.

Changed

  • drive as a ball_action filter action now returns drive rows labelled drive, not every carry. ball_action without actions does not include drives.
  • ball_actions.drives on the Summary now counts drives, not carries.
  • Report schema_version is now 3. Refresh caches keyed on it.
  • The progressive_carry filter uses 10 m and 5 m toward goal, as documented. It used 9.14 m and 4.57 m.
  • The carry rows and counts are unchanged.

[1.15.0] - 2026-09

A distance estimate and a per-minute rate are offered alongside the detected-only totals, top speed is measured over a sustained second, and the report schema version moves to 2.

Added

  • physical.distance_per_min, distance_per90_m, distance_m_est, minutes_measured and minutes_covered on the Player Report: distance as metres per measured minute, projected over the minutes the player is accounted for. Within about 7-14% of true distance. Null below params.est_seconds_floor measured seconds.
  • physical.distance_bands on the Player Report: the five speed zones merged to four bands (high_intensity and sprinting combined into high_speed), each with its distance, per-minute rate and estimate.
  • teams[].physical on the Team Report: the per-player estimate summed over each team, with n_players_est.
  • total_distance_m_est on the Summary player stats: the same estimate at the team-list level. Null below 300 s of measured time.
  • params.est_seconds_floor on the reports.

Changed

  • physical.top_speed_kmh (and Summary max_speed_kmh) is now the highest 1-second rolling-mean speed over contiguous measured windows, not the 99th percentile of samples. Samples above params.top_speed_ceiling_kmh (36 km/h) are dropped first as tracking artifacts, so top speed no longer reports physically impossible values from identity swaps.
  • Report schema_version is now 2. The detected-only distances and speed zones are unchanged. Refresh caches keyed on it.

[1.14.0] - 2026-09

Match periods come from the analysis itself, quality numbers are served per analysis, and the tracking data has a small format.

Removed

  • Team sheets. The PUT /videos/{id}/teamsheet endpoint is gone, and GET /videos/{id}/meta no longer returns roster or player data. Team identity now comes from the confirm-sides widget and your left_start_team assertion.

Added

  • event_types[].quality and eval_sources on Schema: how well each event type was detected for this analysis's engine version, per evaluation slice.
  • format=clean on Download Tracking: the smoothed tracklets and kit colours behind minimap and overlay views, around 1-2 MB against 20-50 MB for the parquet. Available on analyses from version 0.5.38.
  • timeline_confidence on Events match_timeline rows: 0-1, 1.0 on full_game, lower on a detected half split. Null on every other event type and on analyses before engine version 0.5.45.

Changed

  • The half boundary behind first_half / second_half (on Events, the match_timeline filter of Query Intervals, and the attacking-direction rotation of both reports) is now the one the analysis carries, on analyses from engine version 0.5.45. It was re-derived from tracking, which could place half-time a few minutes away from where the possession labels were corrected. Older analyses keep the derived boundary.

Deprecated

  • format=json on Download Tracking: still served, to be removed. Use format=clean to draw the match and format=parquet to analyse it.

[1.13.0] - 2026-09

Two Query Intervals filters classify carries, and the carry event exposes the engine's 0.5.45 movement metadata.

Added

  • progressive_carry filter: carries that progressed the ball toward the opponent goal. See Derived Carry.
  • take_on_candidate filter: carries that engaged a defender, with a tunable max_opp_dist_m. See Derived Carry. Requires engine 0.5.45.
  • carry metadata fields direction_deg, path_length_cm and min_opp_dist_m, queryable through the Event Property filter and described on Schema. Present on analyses written by engine 0.5.45 or later.

[1.12.0] - 2026-09

The drive event is renamed carry. The old name keeps working as a deprecated alias.

Changed

  • The ball action event drive is now carry everywhere it is returned: the event_type and label on Events and Query Intervals, and the Summary event histograms. Analyses written before engine version 0.5.45 used drive; they are served as carry, so one name covers every analysis.

Added

  • ball_actions.carries on the Summary player stats.

Deprecated

  • drive as a ball_action filter action value: accepted as an alias of carry, to be removed. Requests naming it return rows labelled carry.
  • ball_actions.drives on the Summary: kept equal to carries, to be removed.

[1.11.0] - 2026-08

One point shape across both reports, and every threshold the numbers turn on is now in params.

Added

  • params.sequence_gap_ms and params.sequence_shot_slack_ms on both reports: the maximum gap between two possessions in one on_ball.best_sequence, and the maximum time a possession may close after a shot and still count as preceding it.

Changed

  • Player Report position.avg_position[] is now { period, position: { x, y }, n_frames }, previously flat x / y.
  • Team Report teams[].average_positions[].avg_position is renamed position, giving both reports one point shape under one field name.
  • on_ball.shots[].distance_to_goal_m is now null whenever coordinate_frame is not "attacking"; it was previously measured in "attacking_first_half" too, where second-half shots are mirrored and the distance was to the goal the player was defending.
  • Team names fall back to the neutral "Team 0" / "Team 1", never to the wire label "team_0".
  • territory[].zone is documented as a pass-through of the engine's ball_pitchzone vocabulary, so do not switch on it exhaustively.

Unchanged

  • Every other field name, unit and null rule on both reports, and every legacy endpoint.

[1.10.0] - 2026-08

Where two endpoints report the same quantity, they now compute it the same way. See Speed & Distance and Pitch Coordinates.

Added

  • quality on GET /analyses/{id}/meta-data, the same typed subset (score, grade, reasons) the Team Report ships, and null on analyses without the quality artifact.
  • coordinate_frame request parameter on POST /analyses/{id}/query-intervals, either "camera" (default, unchanged) or "attacking", which rotates include_pitch_coords output with the same team sides the reports use and tags each row with the frame applied. See Query Intervals.
  • physical.source on the Player Report, engine or tracking_fallback, naming which smoother produced the speed series.
  • orientation_source on both reports, recording where the attacking direction behind coordinate_frame came from: hub (your confirmed side, or the derived left_start_team), game_meta (the analysis's own record), tracking (inferred from which end each team occupied), or null when neither could tell, in which case coordinate_frame is camera.

Changed

  • Analyses without per-frame speed data now get a full physical section on the Player Report, computed by insight's fallback smoother over tracking positions: distance, speed zones, top speed, fatigue curve and sprints, where they previously returned zeros. Treat tracking_fallback numbers as indicative.
  • Player physics on Summary now come from the same speed series as the Player Report, so a player's distance, top speed and speed buckets match across the two; values shift slightly with the shared estimator, and max_speed_kmh is the 99th percentile of the per-sample speed.
  • tracking_duration_sec on Summary now measures detected time only (detected samples times 40 ms), so it drops on non-broadcast footage where it previously included off-camera gaps.
  • Player Report speed-zone edges are now 7 / 14 / 21 / 25.2 km/h, a refinement of the Summary buckets (7.2 / 14.4 / 19.8 / 25.2), so a Summary bucket is an exact union of report zones.

Unchanged

  • Every live wire shape is untouched: Summary bucket keys and _sec suffixes, team_id strings, filter vocabulary, and the default camera query-intervals output are byte-identical.

[1.9.0] - 2026-08

Added

  • Match reports, per-player and per-team JSON, one call per page.
    • GET /analyses/{id}/player-report/{player_id} returns coverage, physical output (distance, speed zones, top speed, fatigue curve, sprints), positioning (heatmaps, average position, zone hull, third timeline), on-ball actions (passes, shots, carries, tackles, blocks, pass network, touches, involvement, best sequence) and interactions (pressure received and applied, direct opponent, link-up). See Player Report.
    • GET /analyses/{id}/team-report returns average positions per team, possession split, territory, kits and analysis quality. See Team Report.
    • Coordinates are metres in named x / y fields, rotated so each team attacks from left to right, and coordinate_frame reports how far that rotation could be resolved (attacking, attacking_first_half, camera).
    • Every heatmap is the same self-describing grid (bins, extent, counts), and params echoes back every threshold the numbers were computed with.
    • Every on-ball action row carries event_id and t_ms, the join keys to Events and Query Intervals.
    • schema_version (1 at this release) versions the response shape and the thresholds, so key your cache on it.

Changed

  • GET /analyses/{id}/events rows now also carry event_id, the join key the report's on-ball rows use; it is null on older analyses, and existing fields are unchanged.

Unchanged

  • Existing endpoints, filter vocabulary, player keys (0-7, 0-GK) and units are untouched.
  • The report's possession figures are the ones Summary returns, and its quality object is the shape returned by Get Analysis.

Older analyses

  • Report sections degrade independently rather than failing.
  • Analyses without per-frame speed data returned zeroed physical at this release, replaced in 1.10.0 by a fallback smoother.
  • Analyses without nearest-player data return null interactions.pressure_received and pressure_applied, with direct_opponent and link_up as []; the interactions object itself is always present.
  • Analyses whose attacking direction could not be measured return coordinate_frame: "camera".
  • Per-player event counts are lower bounds, so read coverage alongside them.

[1.8.0] - 2026-08

Added

  • Team identity resolution. See Teams and sides.
    • GET /analyses/{id}/team-mapping resolves team_0 / team_1 to home/away, with names, sides, squads and identity_source.
    • team_zero on video responses (home, away or null) says who team_0 is.
    • GET /videos/{id}/preview-teams returns pre-analysis kit detection (colours and player crops), ready seconds after video creation.
    • POST /videos/{id}/embed-token and the embeddable confirm-sides widget let your user confirm which kit is the home team.
    • PUT /videos/{id}/teamsheet replaces the roster endpoint, and GET /videos/{id}/meta reads the resolved meta. See Game meta. PUT /videos/{id}/teamsheet was removed in 1.14.0.
    • New analysis status waiting_for_meta, with waiting_for in the response, is a short automatic fail-open hold while the hub finishes its own kit pass.
  • Analysis quality: a quality object on GET /analyses/{id} with a score (0-100), a grade (good, fair, degraded or unreliable) and reasons explaining any degradation. See Get Analysis.
  • An analysis workflow guide covering the end-to-end flow.

Changed

  • team_0 / team_1 are shirt-cluster labels, fixed before sides are measured, and no longer guaranteed to mean left- or right-starting.
  • Map those labels via team-mapping or team_zero, and render neutral "Team 0" / "Team 1" labels while identity is null.
  • left_start_team on the video is now a derived value, so stop using it to infer home/away; your own assertion still wins when you send it.
  • Identity corrections apply at read time: a team sheet or confirmation arriving after an analysis is visible in all responses immediately, with no re-processing.

Unchanged

  • Filter vocabulary (team_possession with teams: ["team_0"|"team_1"]), player keys (0-7, 0-GK), and query-intervals request and response shapes.

Older analyses

  • Analyses aggregated before this release have no identity measurement recorded, so team_zero and the role fields read null and labels fall back to "Team 0" / "Team 1". Re-aggregating the analysis resolves it.

[1.7.0] - 2026-07

Added

  • Row status and confidence columns on the tracking download, from analysis version 0.5.36.
    • New columns status, overlap_partner_id, position_conf and identity_conf.
    • Full-squad coverage: off-camera players are now included as 5 Hz projected rows, with pitch coordinates only and no pixel coordinates.
    • For the previous on-camera-only behaviour, filter with df[df['status'].str.startswith('detected')].
    • See Download Tracking.

Changed

  • Duplicate event blocks at segment boundaries are removed, roughly 20 fewer duplicates per game.
  • Match timeline durations are corrected by about 2s per segment.
  • Better camera calibration improved position quality, and the roughly 3% of frames that could not be calibrated are now omitted instead of returned with degraded accuracy.

Unchanged

  • Summary player statistics are still computed from detection-backed rows only, so values stay comparable with earlier versions.
  • Events schema, filter types and all units are unchanged.

[1.6.0] - 2026-03

Added

  • Ball action filter (ball_action).
    • Filters passes, drives (dribbles) and shots.
    • Filters by specific players or teams.
    • Requires analysis version 0.5.10 or later.
    • Responses include player_id, team_id and team_name.

[1.5.0] - 2026-02

Added

  • Video management endpoint documentation.
    • GET /videos lists all videos.
    • POST /videos uploads a new video.
    • GET /videos/{id} gets video details.
    • PUT /videos/{id} updates video metadata.
  • Create analysis endpoint documentation: POST /videos/{id}/analysis creates an analysis for a video.
  • Complete workflow examples for video upload and analysis creation.

[1.4.0] - 2026-02

Added

  • Player names in the Summary endpoint.
    • Each entry in the Summary players array includes a name display label such as "Team A #10", alongside player_id, team_id and stats.
    • For a plain player-id to display-name map, use GET /analyses/{id}/meta-data, which returns an identities object.

[1.3.0] - 2026-02

Added

  • Summary endpoint (GET /analyses/{id}/summary).
    • Comprehensive game overview.
    • Per-minute possession breakdown.
    • Player distance and speed statistics.
    • Analysable duration metrics.

Changed

  • Player stats now include speed buckets (walking, jogging, running, sprinting).

[1.2.0] - 2026-01

Added

  • Tracking download (GET /analyses/{id}/tracking/download).
    • Downloads raw tracking data as Parquet or JSON.
    • Includes all player positions at 25 FPS.

[1.1.0] - 2024-12

Added

  • player_possession filter type.
  • team_side_perspective option for the ball location filter.

Fixed

  • Padding is now correctly applied in query-intervals.

[1.0.0] - 2024-10

Initial Release

  • Events endpoint.
  • Meta-data endpoint.
  • Query intervals with the team_possession, ball_location, player_ball_proximity, match_timeline and scene filters.