Uptimeify Docs
Service Status

Service Status

Public, unauthenticated endpoint that returns Uptimeify's own platform availability — check and alert delivery percentages, component states, and the hash-chained frozen monthly totals.

GET /api/service-status

No authentication required. Returns no customer data — only the platform's own delivery figures, published component states, and the frozen monthly totals behind them.

Example (cURL)

curl -X GET "https://uptimeify.io/api/service-status" \
  -H "Accept: application/json"

Response

{
  "checkDelivery": {
    "pct": 99.97,
    "coveragePct": 99.94,
    "insufficientData": false,
    "windowDays": 90
  },
  "alertDelivery": {
    "pct": 97.31,
    "sampleSize": 2454,
    "endpointFailures": 140,
    "latencyP90Ms": 1727,
    "latencySampleSize": 812,
    "insufficientData": false,
    "latencyInsufficientData": false
  },
  "components": [
    { "key": "de-fra", "displayName": "Frankfurt (DE)", "state": "operational", "coveragePct": 99.94 },
    { "key": "alerting", "displayName": "Alert Delivery", "state": "operational", "coveragePct": 100 }
  ],
  "frozenMonths": [
    {
      "month": "2026-06-01",
      "sliKind": "check_delivery",
      "scopeKey": "mesh",
      "numerator": 4318204,
      "denominator": 4319850,
      "unknownUnits": 612,
      "rowHash": "9f2c1a7e4b8d0f3a6c5e2b1d8a7f4e9c0b3d6a1f8e5c2b9d7a4f1e8c5b2d9a6f"
    },
    {
      "month": "2026-06-01",
      "sliKind": "measurement_coverage",
      "scopeKey": "mesh",
      "numerator": 43176,
      "denominator": 43200,
      "unknownUnits": 24,
      "rowHash": "4d1b8e0cc0a2f7593e6b4c1d8a05f2e7b9c3d6a1f8e5c2b9d7a4f1e8c5b2d9a6"
    }
  ]
}

Notes:

  • checkDelivery.pct and alertDelivery.pct are trailing windowDays figures (currently 90 days), truncated to two decimals — never rounded up.
  • unknownUnits is NOT in the denominator of any percentage on this endpoint. A percentage is measured over what the platform could actually account for; denominator is the expectation over the recorded units and unknownUnits is the expectation over the units that were never recorded — a gap in the per-minute recorder, a stalled rollup. The two are separate axes because "we were unavailable" and "we could not measure" are different facts: folding the second into the first would publish an outage that did not happen and make the figure a measure of the recorder's own reliability.
  • coveragePct is the second half of every claim and is never omitted. It is the share of the window the platform can account for, 0-100, truncated to two decimals — null only when nothing was owed in the window yet. checkDelivery.coveragePct covers the whole mesh; components[].coveragePct is per component and is null for a component that has never been measured at all. For the conservative reading — the share of the window positively confirmed as delivered — multiply the two: pct x coveragePct / 100.
  • From any frozenMonths row you can recompute coverage yourself as denominator / (denominator + unknownUnits). Rows with "sliKind": "measurement_coverage" state it directly in minutes instead: numerator is the minutes recorded, denominator the minutes owed. That kind exists because a day on which the recorder never ran leaves check_delivery at 0/0 — there is no observed rate to price a missing check-minute with — so only a minute-based row keeps such a day visible.
  • Alert delivery is two figures, and neither is a 60-second threshold. alertDelivery.pct is the share of attempted deliveries that eventually got through, and alertDelivery.latencyP90Ms is the p90 time from the incident opening to the first notification that actually left our systems, in milliseconds. The former answers "did it arrive", the latter "how fast" — one number cannot answer both, and the single figure published before this (the share dispatched inside 60 seconds) answered neither: at a median dispatch under one second it could not tell a fast delivery from one that took most of a minute, and it scored a delivery that never arrived exactly like a slow one.
  • alertDelivery.pct counts each delivery once, not each attempt. The unit is one (incident, notification channel) pair; the pair counts as delivered if any attempt for it succeeded. A notification that failed twice and got through on the third attempt is therefore one success — counting attempts instead would publish a worse figure the harder our retry mechanism works. alertDelivery.sampleSize is the number of such pairs. Where a delivery carries no channel id (an owner-address email, the incident-management bridge) the transport type stands in as its identity.
  • alertDelivery.pct counts the failures we are responsible for, and endpointFailures counts the ones we are not. A delivery that never arrived because the receiving endpoint was gone (404, 410), because the credentials you gave us were revoked (401, 403), because your service rate-limited us (429), because it returned a 5xx, or because its host could not be reached at all (DNS failure, refused connection, TLS failure, timeout) is a failure of that endpoint, not of our alerting. Those deliveries leave pct — they are in neither its numerator nor its sampleSize — and are counted in alertDelivery.endpointFailures instead. Failures on our side stay in and count against the figure: a payload one of the chat services rejected as malformed, a channel type our dispatcher had no handler for, our own relay or send-throttle skipping a send, or any failure we cannot confidently attribute to the far end. The tie-break always goes against us — anything unattributable counts as ours, because the opposite rule would let the figure improve every time we failed in a new way.
  • endpointFailures is never suppressed and never omitted. It is a count, so it is exactly true at any sample size and carries no publication floor: it is present even when pct is null. Add it to the failures inside pct and you have every alert that did not arrive in the window, for any reason at all — the exclusion above changes which number a failure is reported in, never whether it is reported.
  • What the alert figures exclude, and what they do not. Incidents that were never owed a notification are outside both: alerting suppressed by a maintenance window, a monitor with no notification channel configured, an incident held back by flap damping, or one that resolved before a notification became due. A delivery whose every attempt failed on our side is inside alertDelivery.pct and counts against it — a failed send is a delivery failure, not an exclusion. One that failed at your endpoint is in endpointFailures instead, per the rule above. It is absent from the p90, because an alert that never left has no dispatch time; charging it one would hide a delivery failure inside a latency figure. alertDelivery.latencySampleSize is therefore the number of incidents that were successfully notified, and is always smaller than sampleSize.
  • No alert figure reaches back past the date measurement began. alertDelivery.pct, endpointFailures and latencyP90Ms are all bounded below at the instant our recorder first wrote a sample — the same instant the public status page prints as "Measuring since". Our notification log holds real history from long before that date; it is not counted, because a figure reaching past the start date printed beside it would be a different claim than the one that date supports. The rows stay in the database; they are simply outside the window.
  • Attempts are counted for 24 hours after the incident's day. A delivery still not through a full day after the incident opened is a failed delivery, whatever happens afterwards. Measured over a real trailing 90 days this bound reclassifies 2 of 2590 delivery pairs, and it reclassifies them against us.
  • Latency is a percentile, so it is neither summed nor frozen. The published p90 is computed over the window's own distribution, not derived from the daily figures behind it: the sum of daily percentiles is not a percentile, and neither is their weighted mean. For the same reason no alert_dispatch_latency_p90 row appears in frozenMonths — a monthly figure that cannot be computed by summing days is better absent than invented. The daily rows exist in our own evidence table, each carrying its own sample size.
  • A percentage is always published together with its sample size, and is withheld when that sample is too small. alertDelivery.pct and alertDelivery.latencyP90Ms are null with the matching insufficientData / latencyInsufficientData flag set to true while fewer than 20 events stand behind them. checkDelivery.pct is null with checkDelivery.insufficientData true until our measured series covers 24 hours: what a minute owes is a per-minute rate and what it delivers is a whole check, so on a short series the figure reads a fraction under 100% with nothing wrong at all. In every case the sample size and coveragePct are still published — they are what explains the absence. A null with the flag false means something different: nothing has been measured yet at all.
  • components[].state is one of operational, degraded, down, unknown. unknown means no recent sample exists for that component; the endpoint never fills the gap with the last known state. A component that is unknown right now is not a component that is down — check its coveragePct to see how much of the window that has been true for.
  • frozenMonths lists every append-only monthly total from platform_sli_month, identified by month + sliKind + scopeKey. sliKind is one of check_delivery, component_availability, alert_delivery, alert_endpoint_failure or measurement_coverage — the dispatch-latency percentile is deliberately not among them (see above). Each row's rowHash chains to the previous row for the same sliKind/scopeKey, and a database trigger rejects any UPDATE or DELETE against the table. If you saved a copy of this response earlier, a rowHash that no longer matches for a given month/sliKind/scopeKey means that month's figures were restated after being frozen.

Common errors

  • 500 Unexpected server error on an internal failure

On this page