Modeling the 2026 World Cup as a Status Page
Disclaimer: I work at incident.io, which sells a Status Pages product. I'm not trying to influence you to buy our status page product. I just think status pages can be a canvas for creative ideas.
TLDR; here's the status page
I'm admittedly a fair-weather fan when it comes to FIFA and football but I have fond memories of huddling by the TV and watching bootleg streams of matches on hot summer days when I was younger. These days, most of the matches happen while I'm working and so I'm not always available to watch them. I could poll ESPN's live article for updates, but manually refreshing a page is exactly the kind of job we invented computers for. Naturally, why not have a server poll for me?
The question was what the server should poll into. And the more I thought about it, the more the answer looked like the thing I stare at everyday anyway: a status page. Status pages are a public good. They're uptime monitoring for your platform available for the rest of the internet, and they help contribute to netizens being good neighbors to each other when stuff inevitably breaks. They also already have the exact machinery a goal-notification service needs: subscribers, email fanout, timelines, and per-team components.
The mapping turned out to be suspiciously clean. Each team is a component in the Catalog (in incident.io the Catalog is simply a place where you can define typed enumerations of data that you can use across the platform). Each match is an incident. And component statuses play into the outage metaphor operational when the team is not in a match, degraded_performance when it's an active match, and full_outage when you're eliminated, which sticks, permanently, because there is no recovering from a knockout loss. As teams get eliminated their "uptime" metric goes down. The page becomes a king-of-the-hill leaderboard.
And with component statuses, there's status page incident statuses. A status page incident moves through investigating -> identified -> monitoring -> resolved, and if you squint, so does a match: investigating is pre-match, identified is active play, monitoring is any pause (half-time, a hydration break, the breather before extra time), and resolved is the final whistle. Goals are update messages posted under whatever status the match is currently in, the same way you'd post "still seeing elevated error rates" under an ongoing incident. A typical match works out to about 5–7 emails for a subscriber.
Even VAR fits, and it's my favorite bit. Sportmonks (my data provider more on them in a second) emits real VAR event types, so a GOAL_UNDER_REVIEW flips the incident to monitoring and a GOAL_CONFIRMED flips it back to identified with the new score. The nastier case is a disallowed goal, because Sportmonks doesn't emit a retraction, it just deletes the goal event from the feed. The server keeps a map of every goal event it has seen, and if a known goal vanishes from the feed for two consecutive polls (debounced, because feeds hiccup), it posts a retraction to the status page.
The data flow is simple, if a bit involved:
- The server polls Sportmonks and turns match events into alert payloads.
- Each payload goes to incident.io's generic HTTP alert source.
- The alert route groups alerts by fixture ID (with a grouping window of about four hours), so a new match opens a new alert group.
- The alert group creates an incident.
- The incident triggers a workflow.
- The workflow creates and updates a status page incident and refreshes on every goal, every new phase of the match, and the final whistle.
Above all the per-match incidents sits one long-standing status page incident that tracks the knockouts as a whole, updated every time a match incident resolves.
The polling side has its own set of constraints to consider as well. Sportmonks provides a REST API for realtime data (no one at this price level seems to have webhook support.) The trial tier caps you at 1500 API calls a day, on days that can have four simultaneous matches. So the server runs three cadences: a schedule sync every few hours that learns the fixture list (i.e it discovers matches on its own), a pending watch every few minutes for fixtures near kickoff, and a live poll every few seconds that makes one batched /livescores/inplay call feeding every live match at once. When nothing is on, it burns no budget at all. And it's all running on the cheapest Fly.io machine config (one shared-cpu-1x with 256MB of RAM.)
In building this, I'm surprised how well some of the basic primitives of a status page map onto this domain. Some of it is admittedly a bit hacky and the server is doing a bunch of orchestration work that maybe it shouldn't have to. But I'm happy with the result!
You might think, what's the point of this if you're just going to be watching the game anyway? But in my experience, the data often rolls in faster than the broadcast. The result is delightful blip of suspense upon getting that email notification on your phone, 10 seconds before you're seeing what happens.
Did Vozinha block another shot? Did Morocco score that penalty kick? Absorb the moment, find the crowd's roar, move fast when the defense breaks.