# Detect Football Fixture Kickoff Changes by Match ID

A fixture list can change order when kickoff times change. Comparing yesterday's first row with today's first row can therefore report a completely different match as rescheduled. Index both snapshots by stable match ID, then compare the fields that matter to your application.

By Stats API Editorial (AI-assisted) · Reviewed by Codex automated editorial review · Published 2026-10-28 · Last reviewed 2026-10-11

Canonical URL: https://stats-api.com/blog/detect-football-fixture-kickoff-changes

## Compare identities, not row positions

A fixture list can change order when kickoff times change. Comparing yesterday's first row with today's first row can therefore report a completely different match as rescheduled. Index both snapshots by stable match ID, then compare the fields that matter to your application.

For a calendar view, starts_at and status are useful change fields. Keep the original and replacement values together so a user can see the nature of the change. A score update is a separate concern and should not generate a kickoff-change alert.

## Classify additions and missing observations carefully

A match newly present in your selected window is an addition to that snapshot, not necessarily a newly created sporting fixture. It may have moved into the date window. Likewise, a missing row may have moved outside the filters or reflect an incomplete fetch. Do not label absence as cancellation unless you have an explicit returned status or another verified basis.

The example reports added, missing and changed IDs separately. Its synthetic changed fixture moves kickoff by one hour while retaining identity. A second fixture is absent from the new snapshot and remains a missing observation, with no invented cancellation story.

## Run the reproducible example

This Python 3 example uses deliberately synthetic inputs, not a downloaded match or a forecast for a real fixture. Save it as example.py and run python3 example.py. It uses only the standard library and its assertions check the behavior discussed here. Replace the fixture inputs with validated API records when connecting your application; demo identifiers are not production lookup values.

**Complete offline example**

```python
from datetime import datetime

def instant(value):
    stamp = datetime.fromisoformat(value.replace("Z","+00:00"))
    if stamp.tzinfo is None:
        raise ValueError("timezone required")
    return stamp

def compare(before, after):
    old,new = ({r["id"]:r for r in rows} for rows in (before,after))
    changed = sorted(k for k in old.keys() & new.keys()
        if instant(old[k]["starts_at"]) != instant(new[k]["starts_at"])
        or old[k]["status"] != new[k]["status"])
    return {"added":sorted(new.keys()-old.keys()),"missing":sorted(old.keys()-new.keys()),"changed":changed}

before = [{"id":"match_a","starts_at":"2026-10-20T18:00:00Z","status":"scheduled"},
          {"id":"match_b","starts_at":"2026-10-20T19:00:00Z","status":"scheduled"}]
after = [{"id":"match_a","starts_at":"2026-10-20T19:00:00+00:00","status":"scheduled"}]
assert compare(before,after) == {"added":[],"missing":["match_b"],"changed":["match_a"]}
same = [dict(before[0], starts_at="2026-10-20T18:00:00+00:00")]
assert compare(before[:1],same)["changed"] == []
print(compare(before,after))
```

## Read the result

The comparison identifies one changed kickoff and one missing observation. It does not call the missing fixture canceled. A second assertion confirms that equivalent UTC timestamp spellings do not create a false change.

## Compare only complete compatible snapshots

Retain filters, pagination completion and retrieval timestamps with each snapshot. Comparing a full season with a one-week window creates meaningless missing rows. A failed page walk must not replace the previous complete snapshot or trigger dozens of false removal notifications.

Use offset-aware timestamp parsing before comparing instants. Equivalent representations such as a Z suffix and a +00:00 offset should not produce a change alert merely because their strings differ. The code normalizes timestamps for comparison while preserving their original values in the source records.

## Make notifications replay-safe

Persist a change fingerprint and its delivery state if your product sends calendar alerts. Re-running the same comparison should not notify a user twice. A later correction should create a new reviewed change rather than mutating an already sent message invisibly. Keep the delivery mechanism separate from the comparison function so tests cannot accidentally contact users.

Test reordered rows, equivalent timezone offsets, changed kickoff, changed status and a partial fetch. Begin with a dry-run change report and inspect it against the retained responses. Only then connect it to user-facing notifications under your application's preferences and delivery policy.

## Connect this to your project

Use this report behind a calendar or fixture widget, with snapshot completeness verified before every comparison. The time-awareness guide explains the related display concerns, while the match contract supplies the exact lifecycle values your application should preserve.

- [Read match filters and status](https://stats-api.com/docs/endpoints/matches)
- [Explore the football API](https://stats-api.com/football-api)
- [Keep kickoff and status separate](https://stats-api.com/blog/time-aware-football-ai-utc-status-freshness)
