Read current status and the incident ledger as JSON. No key required
Public status API
No API key
A status page is public information by design, so reading it is public too. These two endpoints need no authentication or key: they are the same data your public page itself renders.
Writing is a different matter: see the Incident API for creating and updating incidents. For a rendered badge or an RSS feed rather than JSON, see Badges & feeds.
Base URL
Your own page's hostname, not ours: your subdomain, or your custom domain once it is live:
https://status.example.com/api/public/{slug}The slug appears twice, and it must be the slug belonging to that hostname. Asking one page's host for a
different page's slug returns 404 rather than that page's data.
GET/summary
Current state and uptime history for every service on the page.
curl https://status.example.com/api/public/{slug}/summaryconst response = await fetch('https://status.example.com/api/public/{slug}/summary');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const summary = await response.json();{
"slug": "acme",
"lastChecked": "2026-08-20T21:02:09.028Z",
"monitoringPaused": false,
"services": [
{
"name": "Checkout API",
"names": {
"en": "Checkout API",
"ar": "واجهة الدفع"
},
"url": "",
"icon": "https://status.example.com/icons/checkout.svg",
"slug": "checkout-api",
"status": "up",
"uptime": "99.98%",
"uptimeDay": "100.00%",
"uptimeWeek": "99.95%",
"uptimeMonth": "99.98%",
"uptimeYear": "99.98%",
"time": 142,
"timeDay": 138,
"timeWeek": 141,
"timeMonth": 142,
"timeYear": 142,
"dailyMinutesDown": {
"2026-08-14": 7
},
"dailyHealth": {
"2026-08-14": {
"rate": 0.486
}
}
}
]
}| Field | Notes |
|---|---|
url | Retained as an empty string for compatibility. Monitor destinations and request credentials are private, including in history and response-time representations. |
lastChecked | When the most recent probe on this page ran. null
if nothing has been checked yet. |
monitoringPaused | true when the page is up but checks are not
running, which means an account with no active subscription. Uptime figures stop advancing; they are not
wrong, just no longer moving. |
name / names | The service name in the page's default language,
and in every language it publishes. Use names if you render more than one. |
slug | The stable identifier. Use this to match a service across calls, never the name, because names get edited and translated. |
status | "up" or "down". |
uptime* | Percentages, as preformatted strings including the
%. |
time* | Average response time in milliseconds, as numbers. |
dailyMinutesDown | Minutes down per day, keyed YYYY-MM-DD. Days
with no downtime are absent, not zero. |
dailyHealth | Per day, the failed-check percentage as
rate (0–100). A day absent from this map was not measured,
which is not the same as a day with no failures, and your own rendering should keep them
distinct. |
How far back the daily maps go follows the page's own history window (7 to 90 days, see Branding).
?include=response-time
Ask for the daily response-time trend as well. Without it the response uses the default service fields;
any other include value is a
400 rather than a silent fallback to the default.
curl https://status.example.com/api/public/{slug}/summary?include=response-timeconst response = await fetch('https://status.example.com/api/public/{slug}/summary?include=response-time');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const summary = await response.json();{
"responseTime": {
"windowDays": 30,
"averageMs": 142,
"points": [
{
"day": "2026-08-18",
"averageMs": 138
},
{
"day": "2026-08-19",
"averageMs": 151
}
]
}
}This object is added to each service. What it measures, exactly:
| Field | Notes |
|---|---|
windowDays | The page's own history window, the same 7–90 days the daily maps cover. |
averageMs | The whole period's average, weighted by how many measurements each
day actually took, not an average of the daily averages, which would let a quiet day count as
much as a busy one. null when the period measured nothing. |
points | One entry per day that measured something, oldest first, each a whole number of milliseconds. Days with nothing to average are absent, not zero. |
Only completed outbound checks count. A check inside an announced maintenance window is excluded, exactly
as it is excluded from uptime; a cron heartbeat has no outbound duration to measure at all and always
reports an empty series. A service with nothing eligible in the period returns
"averageMs": null with no points, never 0, which on a latency figure
would read as "instant" rather than "not measured". History begins when this representation was first
published: earlier days were never recorded in a form that could be separated from maintenance, and they
are left out rather than estimated.
GET/incidents
The incident ledger: open and resolved, incidents and scheduled maintenance alike.
curl https://status.example.com/api/public/{slug}/incidentsconst response = await fetch('https://status.example.com/api/public/{slug}/incidents');
if (!response.ok) throw new Error(`InBrief responded ${response.status}`);
const ledger = await response.json();{
"slug": "acme",
"incidents": [
{
"id": "inc_example_01",
"type": "incident",
"services": [
"checkout-api"
],
"startedAt": "2026-08-14T09:00:00.000Z",
"resolvedAt": "2026-08-14T09:41:00.000Z",
"publishedAt": "2026-08-14T09:00:10.000Z",
"scheduledEndAt": null,
"maintenanceState": null,
"title": {
"en": "Elevated error rates"
},
"summary": {
"en": "We are investigating elevated error rates."
},
"updates": [
{
"id": "update_example_01",
"status": "identified",
"body": {
"en": "A failover has been triggered."
},
"createdAt": "2026-08-14T09:12:00.000Z"
}
]
}
]
}| Field | Notes |
|---|---|
type | "incident" or "maintenance". Scheduled
maintenance is the same object with a future startedAt. |
services | Monitor slugs this incident was attached to. Empty when it was not attached to any. |
resolvedAt | null while the incident is still
open. |
title / summary | Keyed by language code. Only languages the page publishes are present. |
updates | Oldest first, because it is a timeline. Each carries
a status of investigating, identified, monitoring
or resolved. Empty for an incident posted without updates. |
Caching
Both endpoints answer with cache-control: public, max-age=60. Poll no faster than that; a
shorter interval gets you the same response from a cache, not fresher data.
The ?include=response-time representation is cached differently, because it is what the
status page itself reads on a timer:
cache-control: public, max-age=0, s-maxage=30, must-revalidate with a strong
ETag. Send it back as If-None-Match and an unchanged page answers
304 with no body. The two representations are separate cache entries; asking for one never
returns the other.
If you want push rather than poll, webhooks deliver these same events to your endpoint as they happen.
Errors
| Status | Means |
|---|---|
404 | The slug in the path does not belong to the hostname you asked, or no
such page exists. Body is {"error": "Not found"}. |
There is nothing else to handle. No authentication means no 401, and no plan gate means no
403.