There's no standard status page, and other lessons from tracking 96 providers - DEV Community
Add reaction
Like
Unicorn
Exploding Head
Raised Hands
Fire
Jump to Comments
Save
Boost
More...
Copy link<br>Copy link
Copied to Clipboard
Share to X
Share to LinkedIn
Share to Facebook
Share to Mastodon
Share Post via...
Report Abuse
Your Slack won't load. Is it you, your VPN, the office wifi, or Slack? You open Slack's status page and it's green. It's often green while you're clearly down, because a company updates its own status page after it confirms an incident — not the moment you feel it.
I spent the last few months building OutageDeck, which reads the official status source of 96 cloud and SaaS providers and folds them into one place. I assumed the hard part would be the frontend. It wasn't. The hard part was that "official status" is a swamp. Here's what I found in it.
The short version:
There is no standard status format. Statuspage is common, but the tail is bespoke — Google, Slack, Heroku, Azure over RSS, and AWS in UTF-16.
Providers' own timestamps lag, sometimes by days, so you can't compute uptime from them.
Some official feeds are stale fossils, or model thousands of instances, so "is it up?" has no single answer.
Official status is authoritative but late — which is the whole reason crowd-sourced "is it down?" sites exist.
All of this pushed me toward a deliberately tiny backend: one Next.js app and a Postgres database, no queue, about $55/month.
There is no standard
Atlassian Statuspage is the closest thing to a common language. Around 89 of my 96 providers expose a Statuspage-style summary.json and incidents.json, and one adapter reads all of them. Some providers don't even run Statuspage but copy its shape anyway — incident.io serves a Statuspage-compatible endpoint on each customer's own domain, so Notion and Linear parse with the exact same code.
Then the long tail starts. Google publishes an incidents.json in its own format (shared across Cloud, Workspace, and Firebase). Slack has a bespoke API. Heroku is on the fourth version of its own. Azure only offers RSS. AWS ships an event feed encoded in UTF-16 with a byte-order mark , which silently breaks any parser that assumes UTF-8. "Just read the status page" turned into six adapters and a pile of encoding checks.
The timestamps lie
My first uptime numbers were nonsense, and it took me a while to see why: I was computing them from each provider's own captured_at timestamp, and those lag. One provider's was days behind at one point. If a feed says a state was "captured three days ago," you can't tell whether the service was down for three days or the timestamp is just stale.
The fix was to stop trusting upstream time entirely. Every observation is stamped with my check time, and uptime is computed from when I saw a state, not when the provider says it happened. The corollary matters too: if my own poller has a gap — a deploy, say — I don't backfill it as "up." Any gap longer than 30 minutes is recorded as no data, because inventing green is worse than admitting I wasn't looking.
Some official sources are fossils
A few providers technically have a machine-readable status endpoint that you still can't use. Stripe's legacy status JSON has been effectively frozen since 2024; it will cheerfully report that everything is fine, forever. Salesforce's status API models nearly 3,900 separate instances, so "is Salesforce up?" depends entirely on which pod your org sits on.
For sources like these, the honest move is to not ship them until I have something I'd trust myself. That's why they're still missing from the list. I'd rather explain a gap than fake a green light.
What the feeds did to the backend
Two properties of these feeds shaped the whole system.
They re-send their full recent history on every poll, and an unchanged upstream just re-stamps the same row. So the raw data can't reconstruct history — you can't diff yesterday back out of it. Instead of trying to rederive it later, I roll each poll's observations into daily per-provider buckets at write time. I also skip writing rows that didn't change; an early version rewrote identical rows every run and burned the database's IO budget for nothing.
I run two schedulers on purpose: a 10-minute cron as the primary, and a 30-minute job as a backstop. So runs overlap. Rather than add a queue or a lock service, I let Postgres be the referee. Alert deliveries are claimed with a unique constraint before anything goes out:
-- alert_deliveries has: unique (subscription_id, event_key)<br>insert into alert_deliveries (subscription_id, event_key, payload)<br>values ($1, $2, $3)<br>on conflict (subscription_id, event_key) do nothing;<br>-- 1 row inserted -> I won the claim, send the alert<br>-- 0 rows inserted -> another run already has it, skip
Enter fullscreen mode
Exit fullscreen mode
The insert that survives the unique index is the one that sends; the overlapping run writes nothing and...