Documentation
Quickstart
Paging incident history, and reading uptime without dropping its denominator. Both are written out in full because both are places a caller gets a plausible wrong answer rather than an error.
Walking the history
limit stops at 200 and one venue having a bad week exceeds that on its own, so the whole history is a walk. The cursor is before — a timestamp, not an offset, because new incidents are inserted at the front of the ordering and an offset would skip a row every time one opened mid-walk.
const BASE = 'https://api.up24.app';
async function* incidents(venue, days = 90) {
let cursor;
for (;;) {
const url = new URL(`${BASE}/v1/incidents`);
url.search = new URLSearchParams({ venue, days, limit: 200, ...cursor });
const res = await fetch(url);
// The one status worth handling: the API says when to come back.
if (res.status === 429) {
const wait = Number(res.headers.get('retry-after') ?? 5);
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (!res.ok) throw new Error(`up24 ${res.status}`);
const { incidents: page } = await res.json();
if (page.length === 0) return;
yield* page;
// A short page is the end. The cursor is the last row's start and id,
// never an offset: new incidents are inserted at the front, and incidents
// opened in one tick share a start.
if (page.length < 200) return;
const last = page.at(-1);
cursor = { before: last.startedAt, beforeId: last.id };
}
}
for await (const incident of incidents('binance')) {
console.log(incident.slug, incident.severity, incident.durationMs);
}The one status worth handling is 429: the API says when to come back in retry-after, and that header is the whole of the backoff you need. Nothing else here is retryable in a way you can guess at.
Quoting uptime honestly
Every number here is a measurement with a denominator attached. “99.98% uptime” without the check count or the window is not a claim up24 makes anywhere on this site, and anything named typical is a median of daily percentiles rather than the window's own — percentiles do not compose.
const BASE = 'https://api.up24.app';
// Uptime with its denominator, which is the only way up24 quotes one.
const res = await fetch(`${BASE}/v1/reliability?days=90`);
if (res.status === 429) {
const wait = Number(res.headers.get('retry-after') ?? 5);
throw new Error(`rate limited, retry in ${wait}s`);
}
const { venues } = await res.json();
for (const v of venues) {
const { uptimePct, checks, measuredDays, days, typicalP95Ms } = v.summary;
// measuredDays beside days on purpose: a venue watched for eight days of
// ninety has not been 99.99% for a quarter.
console.log(
`${v.name}: ${uptimePct?.toFixed(2)}% of ${checks} checks ` +
`over ${measuredDays}/${days} days, typical p95 ${typicalP95Ms}ms`,
);
}measuredDays is printed beside days on purpose: a venue watched for eight days of ninety has not been 99.99% for a quarter, and a dashboard that drops the first number is publishing a claim the data does not support.