<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kerolos Atallah</title>
    <description>The latest articles on DEV Community by Kerolos Atallah (@kerolos_atallah).</description>
    <link>https://dev.to/kerolos_atallah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034583%2F2f750b6b-acd5-4eb2-a291-962e979b8e98.jpg</url>
      <title>DEV Community: Kerolos Atallah</title>
      <link>https://dev.to/kerolos_atallah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kerolos_atallah"/>
    <language>en</language>
    <item>
      <title>How to monitor third-party provider outages with two signals</title>
      <dc:creator>Kerolos Atallah</dc:creator>
      <pubDate>Sat, 08 Aug 2026 19:36:00 +0000</pubDate>
      <link>https://dev.to/kerolos_atallah/how-to-monitor-third-party-provider-outages-with-two-signals-1600</link>
      <guid>https://dev.to/kerolos_atallah/how-to-monitor-third-party-provider-outages-with-two-signals-1600</guid>
      <description>&lt;p&gt;When production starts failing, one of the first questions is also one of the most expensive to answer badly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is our application broken, or is a provider we depend on having an incident?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the dependency is AWS, Cloudflare, GitHub, OpenAI, Stripe, or another cloud or SaaS vendor, there are two common approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the provider's official status page.&lt;/li&gt;
&lt;li&gt;Probe the service yourself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither is enough on its own. A useful monitoring setup treats them as separate signals.&lt;/p&gt;

&lt;p&gt;I am Kerolos, the founder of &lt;a href="https://outagedeck.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=two_signal_provider_monitoring" rel="noopener noreferrer"&gt;OutageDeck&lt;/a&gt;. I built it to normalize official status sources across 172 providers. This post explains where that signal fits, where it does not, and how I would combine it with synthetic checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 1: what the provider says
&lt;/h2&gt;

&lt;p&gt;An official status feed tells you what the vendor has acknowledged. It can provide details a black-box probe cannot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the affected product or service&lt;/li&gt;
&lt;li&gt;the region or customer segment involved&lt;/li&gt;
&lt;li&gt;investigation and mitigation updates&lt;/li&gt;
&lt;li&gt;the incident timeline&lt;/li&gt;
&lt;li&gt;a recovery announcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is timing and scope. A provider may not publish an incident immediately. Its dashboard may say that the platform is operational while your account, region, route, or workload is failing.&lt;/p&gt;

&lt;p&gt;Official status is valuable evidence, not an oracle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 2: what your users experience
&lt;/h2&gt;

&lt;p&gt;A synthetic check asks a different question: can a request complete successfully from a location you care about?&lt;/p&gt;

&lt;p&gt;Useful probes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an HTTP request against a critical endpoint&lt;/li&gt;
&lt;li&gt;a DNS lookup through the route your users take&lt;/li&gt;
&lt;li&gt;a small authenticated API transaction&lt;/li&gt;
&lt;li&gt;a read and write against a non-production object or record&lt;/li&gt;
&lt;li&gt;a check from more than one region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The probe should exercise the smallest safe transaction that represents the dependency. A TCP connection to port 443 proves very little if the API is returning errors after authentication.&lt;/p&gt;

&lt;p&gt;Synthetic monitoring also has blind spots. Your probe may fail because of your own credentials, network, DNS, deployment, or test data. It can tell you that a path is broken, but not always who owns the fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  The useful part is the correlation
&lt;/h2&gt;

&lt;p&gt;Put the two signals together and the incident becomes easier to triage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider status&lt;/th&gt;
&lt;th&gt;Synthetic probe&lt;/th&gt;
&lt;th&gt;Working assumption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Operational&lt;/td&gt;
&lt;td&gt;Passing&lt;/td&gt;
&lt;td&gt;No provider incident visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident&lt;/td&gt;
&lt;td&gt;Passing&lt;/td&gt;
&lt;td&gt;Provider issue may not affect your path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational&lt;/td&gt;
&lt;td&gt;Failing&lt;/td&gt;
&lt;td&gt;Investigate your stack, but watch for delayed provider acknowledgement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident&lt;/td&gt;
&lt;td&gt;Failing&lt;/td&gt;
&lt;td&gt;Strong evidence of an upstream dependency incident&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is a starting point, not an automatic root-cause verdict. Region, service, and account scope still matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small provider-status check
&lt;/h2&gt;

&lt;p&gt;OutageDeck exposes a keyless read-only API, so a quick check can be as small as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; https://outagedeck.com/api/v1/providers/aws &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'{
      provider: .data.name,
      status: .data.currentStatus.code,
      headline: .data.currentStatus.headline,
      checked_at: .data.source.checkedAt,
      source: .data.source.officialUrl
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a dependency stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;providers&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;aws cloudflare github openai stripe&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;provider &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; &lt;span class="s2"&gt;"https://outagedeck.com/api/v1/providers/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;provider&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'[
        .data.name,
        .data.currentStatus.code,
        .data.currentStatus.headline,
        .data.source.checkedAt
      ] | @tsv'&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source timestamp matters. A green result from stale data should not be treated as healthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  A matching synthetic check
&lt;/h2&gt;

&lt;p&gt;The independent side might start with an HTTP request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--fail&lt;/span&gt; &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--show-error&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-time&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'status=%{http_code} total=%{time_total}s\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://your-critical-endpoint.example/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production I would add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;at least two probe locations&lt;/li&gt;
&lt;li&gt;consecutive-failure thresholds to reduce transient noise&lt;/li&gt;
&lt;li&gt;a dependency-specific timeout&lt;/li&gt;
&lt;li&gt;a safe authenticated transaction where possible&lt;/li&gt;
&lt;li&gt;separate alerts for probe failure and vendor acknowledgement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not make the provider-status lookup a hard dependency of the probe itself. If the aggregator is unreachable, the probe should still report what it observed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical alert policy
&lt;/h2&gt;

&lt;p&gt;I would route the combinations differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Probe fails, provider reports an incident:&lt;/strong&gt; page the on-call owner with upstream context attached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probe fails, provider reports operational:&lt;/strong&gt; page as an unclassified failure and keep polling the provider feed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probe passes, provider reports an incident:&lt;/strong&gt; send a lower-urgency warning to the service owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both pass:&lt;/strong&gt; remain quiet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That keeps a broad vendor incident from creating hundreds of disconnected alerts while preserving the evidence needed for triage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to record during the incident
&lt;/h2&gt;

&lt;p&gt;For each signal, retain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;observation time&lt;/li&gt;
&lt;li&gt;last successful check&lt;/li&gt;
&lt;li&gt;region and service&lt;/li&gt;
&lt;li&gt;response code or normalized state&lt;/li&gt;
&lt;li&gt;provider source timestamp&lt;/li&gt;
&lt;li&gt;incident identifier and updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the post-incident review much better. You can see whether your probes detected the problem before the provider acknowledged it, and whether your alert thresholds helped or slowed the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it without creating an account
&lt;/h2&gt;

&lt;p&gt;I made a no-account &lt;a href="https://outagedeck.com/stack?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=two_signal_provider_monitoring" rel="noopener noreferrer"&gt;dependency stack checker&lt;/a&gt; for the provider side of this workflow. Pick the vendors in your stack and it produces one shareable view of their official status signals. OutageDeck also exposes JSON, RSS, badges, CLI tools, and an MCP endpoint; free accounts can watch up to five providers by email.&lt;/p&gt;

&lt;p&gt;I would like to hear how other teams correlate vendor status with their own telemetry. Which signal has been more misleading in your incidents: a provider dashboard that stayed green, or a synthetic check that blamed the wrong dependency?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>monitoring</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I read 172 official status feeds every 10 minutes. Here is what is quietly broken in them.</title>
      <dc:creator>Kerolos Atallah</dc:creator>
      <pubDate>Wed, 05 Aug 2026 17:00:52 +0000</pubDate>
      <link>https://dev.to/kerolos_atallah/i-read-172-official-status-feeds-every-10-minutes-here-is-what-is-quietly-broken-in-them-4234</link>
      <guid>https://dev.to/kerolos_atallah/i-read-172-official-status-feeds-every-10-minutes-here-is-what-is-quietly-broken-in-them-4234</guid>
      <description>&lt;p&gt;For the last few weeks I've had a cron job reading the official status feed of 172 cloud and SaaS providers every ten minutes. Not scraping their HTML, not counting tweets, just reading the machine-readable source each vendor publishes about itself.&lt;/p&gt;

&lt;p&gt;I wrote about the first version of this in July, when it was 96 providers. This is what another 76 vendors and a month of collected data added, and most of it is stranger than the first batch.&lt;/p&gt;

&lt;p&gt;I expected the hard part to be scale. It wasn't. The hard part is that "the official status feed" is not one thing, and several of them are quietly broken in ways their own vendors seem not to know about.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is no standard, and it isn't close
&lt;/h2&gt;

&lt;p&gt;Statuspage's &lt;code&gt;/api/v2/summary.json&lt;/code&gt; is the closest thing the industry has to a convention, and it covers most but not all of what I track. The rest need their own parsers. Google publishes a dashboard &lt;code&gt;incidents.json&lt;/code&gt; in its own shape, AWS has a health event feed, Azure emits RSS, Slack has a bespoke API, Oracle Cloud has a region-by-service matrix, Status.io has its own API, and Instatus serves a near-copy of Statuspage's format.&lt;/p&gt;

&lt;p&gt;That last one is the instructive case. Instatus exposes &lt;code&gt;/api/v2/summary.json&lt;/code&gt; at the same path as Statuspage, and it parses, but it omits the &lt;code&gt;components&lt;/code&gt; array, &lt;code&gt;page.updated_at&lt;/code&gt;, and the incident archive. A parser written against Statuspage will happily consume it and conclude that a vendor with a live outage has no components and nothing wrong. Compatible-looking is worse than incompatible.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS ships its status feed in UTF-16, big-endian
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;https://status.aws.amazon.com/data.json&lt;/code&gt; redirects to &lt;code&gt;health.aws.amazon.com/public/currentevents&lt;/code&gt; and returns a body that starts with the bytes &lt;code&gt;FE FF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's a UTF-16 big-endian BOM. Not UTF-8. Not even the little-endian UTF-16 you'd occasionally expect from a Windows toolchain. &lt;code&gt;response.text()&lt;/code&gt; in a browser or Node will not give you usable JSON, and neither will a naive UTF-8 decode. You get a string of interleaved null bytes and a parse error that looks like the endpoint is down.&lt;/p&gt;

&lt;p&gt;Everyone consuming this feed has independently discovered this and written the same three-branch decoder. It has apparently been this way for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe's status JSON has said "all services are online" since February 2024
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://status.stripe.com/current
&lt;span class="go"&gt;{"statuses":{"api":"up","webhooks":"up",...},
 "largestatus":"up",
 "message":"All services are online.",
 "time":"February 09, 2024 @ 06:08PM +00:00"}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTTP 200. Well-formed JSON. Reassuring message. And a timestamp from over two years ago that has not moved since. I re-ran that curl the morning I published this, and it still says exactly that.&lt;/p&gt;

&lt;p&gt;Stripe's human status page is fine. They migrated, and the new one works. The old JSON endpoint was simply never turned off, so it still answers, still says everything is up, and will keep saying that through any incident you care about. If you wired a dashboard to it in 2023, it is still green today and it means nothing.&lt;/p&gt;

&lt;p&gt;This is the single strongest argument I know of for not trusting a status endpoint you haven't checked the freshness of. It's also why I refuse to list Stripe at all rather than serve a fossil. A green light that cannot turn red is worse than no light.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vendors forget to close incidents
&lt;/h2&gt;

&lt;p&gt;At the time of writing, AWS still lists two open events in its own health feed, one in me-central-1 and one in me-south-1, opened on 1 and 2 March. That is over 3,700 hours each, about 157 days. Narrow the question to incidents that opened in the last 30 days and the longest is a Telnyx incident at 506 hours, nearly 21 days, followed by a KnowBe4 purchasing page at 463 hours.&lt;/p&gt;

&lt;p&gt;I don't think these are month-long outages. I think they're incidents someone opened, fixed, and never went back to close. But there is no way to tell that from the outside, and the vendor's own page still says open, so any honest aggregator has to either report it as open or start silently overriding vendors about their own systems. I report it, and I label the duration "stayed open" rather than "downtime", because those are not the same claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  The genuinely strange ones
&lt;/h2&gt;

&lt;p&gt;Cisco Duo publishes 847 components with 94 distinct names. &lt;code&gt;Core Authentication Service&lt;/code&gt; appears 80 times, once per deployment shard. If you build your lookup with a naive &lt;code&gt;new Map(components.map(c =&amp;gt; [c.name, c]))&lt;/code&gt;, last wins, and 79 shards reporting an outage get masked by one healthy one at the end of the array. You have to take the worst status across every occurrence.&lt;/p&gt;

&lt;p&gt;ClickHouse has a component named &lt;code&gt;AWS  us-west-2&lt;/code&gt;, with two spaces. Match on the exact string or you will never map it.&lt;/p&gt;

&lt;p&gt;NetSuite once published its internal telemetry as public incidents. Real entries, in the real feed, titled &lt;code&gt;_system_metadata:eyJ1cHRpbWUi…&lt;/code&gt;. Base64-decode that and you get &lt;code&gt;{"uptime":"99.99","requests_served":…}&lt;/code&gt;. Somebody's monitoring pipeline was writing to the customer-facing incident stream.&lt;/p&gt;

&lt;p&gt;Several status pages block non-browser clients. 8x8's WAF returns 403 to anything without a browser user-agent. Okta's status API returns 401. A status page that only a human can read is, for automation purposes, not a status page.&lt;/p&gt;

&lt;p&gt;No consumer carrier publishes one at all. Vodafone, AT&amp;amp;T, Verizon, T-Mobile, BT, Orange, Telstra: I checked all of them. There is no official machine-readable status feed for any major consumer telecom operator. Every site that appears to show you one is showing you crowd reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a month of this actually looks like
&lt;/h2&gt;

&lt;p&gt;Across the last 30 days, the 172 providers logged 1,455 incidents between them, and 151 of the 172 had at least one. 369 were rated major or critical by the vendor themselves: 267 major, 102 critical, against 1,084 minor. Telecom and connectivity was the busiest category with 305, then cloud and infrastructure at 219, then developer tools at 196, then AI at 147.&lt;/p&gt;

&lt;p&gt;The distribution is the part I found most useful. Most of it is minor, and most of what a status page publishes is noise relative to your stack. The question that actually matters during an incident isn't "what's broken somewhere". It's "is anything I depend on broken, right now, according to the vendor itself".&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway I'd want if I were reading this
&lt;/h2&gt;

&lt;p&gt;If you consume vendor status programmatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the timestamp, always. A stale-but-green endpoint is the failure mode you will not notice.&lt;/li&gt;
&lt;li&gt;Assume component names repeat. Aggregate to worst-status, never last-wins.&lt;/li&gt;
&lt;li&gt;Don't trust a compatible-looking payload. Assert that the fields you need exist.&lt;/li&gt;
&lt;li&gt;Decode by bytes, not by assumption. Check for a BOM before parsing.&lt;/li&gt;
&lt;li&gt;Treat "open" as "the vendor hasn't closed it", which is not the same as "still down".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I turned all of this into &lt;a href="https://outagedeck.com" rel="noopener noreferrer"&gt;OutageDeck&lt;/a&gt;, which reads those 172 feeds every ten minutes so you don't have to write the decoders. But honestly, the five rules above are most of the value. If you're building this in-house, that's the list I wish I'd had.&lt;/p&gt;

&lt;p&gt;Happy to answer anything about specific feeds in the comments. If there's a vendor whose format has broken you in an interesting way, I'd like to hear it.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>There's no standard status page, and other lessons from tracking 96 providers</title>
      <dc:creator>Kerolos Atallah</dc:creator>
      <pubDate>Sat, 18 Jul 2026 01:50:09 +0000</pubDate>
      <link>https://dev.to/kerolos_atallah/theres-no-standard-status-page-and-other-lessons-from-tracking-96-providers-56k6</link>
      <guid>https://dev.to/kerolos_atallah/theres-no-standard-status-page-and-other-lessons-from-tracking-96-providers-56k6</guid>
      <description>&lt;p&gt;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 &lt;em&gt;after&lt;/em&gt; it confirms an incident — not the moment you feel it.&lt;/p&gt;

&lt;p&gt;I spent the last few months building &lt;a href="https://outagedeck.com" rel="noopener noreferrer"&gt;OutageDeck&lt;/a&gt;, 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The short version:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;Providers' own timestamps lag, sometimes by days, so you can't compute uptime from them.&lt;/li&gt;
&lt;li&gt;Some official feeds are stale fossils, or model thousands of instances, so "is it up?" has no single answer.&lt;/li&gt;
&lt;li&gt;Official status is authoritative but late — which is the whole reason crowd-sourced "is it down?" sites exist.&lt;/li&gt;
&lt;li&gt;All of this pushed me toward a deliberately tiny backend: one Next.js app and a Postgres database, no queue, about $55/month.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  There is no standard
&lt;/h2&gt;

&lt;p&gt;Atlassian Statuspage is the closest thing to a common language. Around 89 of my 96 providers expose a Statuspage-style &lt;code&gt;summary.json&lt;/code&gt; and &lt;code&gt;incidents.json&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;Then the long tail starts. Google publishes an &lt;code&gt;incidents.json&lt;/code&gt; 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 &lt;strong&gt;UTF-16 with a byte-order mark&lt;/strong&gt;, which silently breaks any parser that assumes UTF-8. "Just read the status page" turned into six adapters and a pile of encoding checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The timestamps lie
&lt;/h2&gt;

&lt;p&gt;My first uptime numbers were nonsense, and it took me a while to see why: I was computing them from each provider's own &lt;code&gt;captured_at&lt;/code&gt; timestamp, and those lag. One provider's was &lt;strong&gt;days&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;The fix was to stop trusting upstream time entirely. Every observation is stamped with &lt;em&gt;my&lt;/em&gt; 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 &lt;em&gt;no data&lt;/em&gt;, because inventing green is worse than admitting I wasn't looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some official sources are fossils
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For sources like these, the honest move is to &lt;em&gt;not&lt;/em&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the feeds did to the backend
&lt;/h2&gt;

&lt;p&gt;Two properties of these feeds shaped the whole system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They re-send their full recent history on every poll,&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I run two schedulers on purpose:&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- alert_deliveries has: unique (subscription_id, event_key)&lt;/span&gt;
&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;alert_deliveries&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;conflict&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="k"&gt;nothing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- 1 row inserted  -&amp;gt; I won the claim, send the alert&lt;/span&gt;
&lt;span class="c1"&gt;-- 0 rows inserted -&amp;gt; another run already has it, skip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The insert that survives the unique index is the one that sends; the overlapping run writes nothing and moves on. No queue, no exactly-once framework, no dead-letter topic. The uptime writes take a per-entity advisory lock for the same reason. The whole thing is one Next.js app and a Postgres database, no background workers, running on about &lt;strong&gt;$55 a month&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built the thing I wanted
&lt;/h2&gt;

&lt;p&gt;OutageDeck is the result: one place to check whether 96 major providers are up, sourced from each one's official status feed rather than from user complaints. It's free to use and there's no signup to check a provider. Under the hood it's the pipeline above — official-only, honest about timestamps, and clear when it just doesn't know yet.&lt;/p&gt;

&lt;p&gt;If you want more than a webpage, there's a public JSON API, embeddable SVG status badges you can drop in a README, RSS feeds per provider, and an OpenAPI 3.1 doc.&lt;/p&gt;

&lt;p&gt;Two honest caveats: incident history only goes as deep as each upstream feed exposes, so this isn't a system of record for ancient outages; and I'm still adding providers as I find sources I actually trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and stay in the loop
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check any provider, free, no signup:&lt;/strong&gt; &lt;a href="https://outagedeck.com" rel="noopener noreferrer"&gt;outagedeck.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Want a heads-up when something breaks?&lt;/strong&gt; Free email alerts for up to 5 providers; paid plans add Slack, Discord, and webhooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer a digest?&lt;/strong&gt; I send a short weekly rundown, &lt;em&gt;This week in cloud outages&lt;/em&gt; — you can subscribe on the site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd genuinely like to know two things in the comments: &lt;strong&gt;which providers you'd want tracked next&lt;/strong&gt;, and &lt;strong&gt;what would make the API useful inside your own monitoring&lt;/strong&gt;. I'll be writing up more of the internals (the uptime rollups, the fail-open API) — follow along if that's your thing.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>devops</category>
      <category>postgres</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
