Documentation

Incidents

Events up24 detected, and their permalinks.

2 endpoints · all GET · no key · samples in curl, Python, JavaScript and Go

GET /v1/incidents

Incident history

const url = new URL('https://api.up24.app/v1/incidents');
url.search = new URLSearchParams({ venue: 'binance', category: 'exchange', status: 'all', days: '90', limit: '50', format: 'json' });

const res = await fetch(url);
if (!res.ok) throw new Error(`up24 ${res.status}`);
const data = await res.json();

Newest first. Page with before, not with an offset: the list is ordered by startedAt descending and new incidents are inserted at the front, so an offset would skip a row every time one opened mid-walk. Pass the startedAt of the last incident you received to get the next page; a short page means you have reached the end.

The 200-row ceiling is real and one venue having a bad week exceeds it on its own, so a caller that wants the whole history has to walk rather than raise limit.

Parameters

venuestringquery

Restrict to one exchange. Omit for the whole fleet.

"binance""bybit""coinbase""kraken""okx""bitfinex""deribit""bitstamp""gemini""cryptocom""hyperliquid""upbit""binance-futures""bybit-futures""okx-futures"

categorystringquery

Only rows of this category. Absent means every category, which is what an unparameterised request has always meant and still means. A category up24 does not measure is not an error: it answers with an empty list rather than a 404, because "nothing here yet" and "no such thing" are different answers and a caller polling for a category up24 is about to add should not have to tell them apart from a status code. Resolved from each incident’s venue through the registry; the incident row itself carries no category, because it is the same object the alert webhook delivers and a new field there is a change to somebody else’s parser.

"exchange""rpc""market-data""oracle""stablecoin""bridge""ramp"

statusstringquery

Open incidents, closed ones, or both.

"open""closed""all"

Defaults to "all".

daysintegerquery

How far back to look.

Defaults to 90.

limitintegerquery

Rows per page.

Defaults to 50.

beforestring (date-time)query

Cursor: only incidents that started strictly before this instant. ISO 8601 UTC.

beforeIdintegerquery
formatstringquery

Response shape. json is the default and is the whole of it; csv is a flat projection of the same object, with content-disposition set so a browser saves it. Every CSV row carries generatedAt, the window, the measured part of the window and methodologyUrl as columns rather than as a header comment — this is the output that ends up in a spreadsheet and gets quoted from a slide six months later, and a caveat a parser strips is a caveat that will not be there when it is needed. Here: one row per incident, with the venue’s own notice beside it where there is one — including subjectMatched, which is false on every pairing.

"json""csv"

Defaults to "json".

Responses

  • 200One page of incidents.
  • 400A query parameter is out of range or malformed.
  • 429Over the origin rate limit.

200 returns IncidentsResponse

generatedAtstring (date-time)
incidentsIncident[]

GET /v1/incidents/{slug}

One incident: timeline, error breakdown, latency around the event

const url = new URL('https://api.up24.app/v1/incidents/binance-2026-10-01-ws');

const res = await fetch(url);
if (!res.ok) throw new Error(`up24 ${res.status}`);
const data = await res.json();

The API behind the permalink. The timeline is built from the incident’s own columns rather than from alert history, which is expired at ninety days — a page that lost half its timeline the night the sweep ran would stop being evidence exactly when the incident became history.

A stream incident charts the venue’s REST latency over the same window, labelled as such: a feed either delivers or it does not, so it has no latency of its own, and the question a reader actually has is whether the rest of the exchange was fine.

Cached 5s while the incident is open and 300s once it has closed — a closed incident is history and stops changing.

Parameters

slugstringpathrequired

The permalink id — {venue}-{YYYY-MM-DD}-{rest|ws}-{degraded|outage}, with -2, -3 for a second incident of the same shape on the same day.

Responses

  • 200The incident, in full.
  • 404No incident has that slug, or its venue is no longer published.
  • 429Over the origin rate limit.

200 returns IncidentDetailResponse

generatedAtstring (date-time)
incidentIncident
venueobject
venue.idstring

"binance""bybit""coinbase""kraken""okx""bitfinex""deribit""bitstamp""gemini""cryptocom""hyperliquid""upbit""binance-futures""bybit-futures""okx-futures"

venue.namestring
venue.symbolstring | null

The instrument up24 probes, in this venue's own notation — XBTUSD on Kraken, tBTCUSD on Bitfinex — not a normalised one, so it matches what you would type into that venue’s own API. Null on any category that trades no instrument, which today is every rpc row.

venue.quotestring | null

The currency the symbol is priced in. Venues differ, so do not compare prices across them naively. Null on any category that trades no instrument, for the same reason symbol is.

componentobject
component.idstring

One REST endpoint or one WebSocket stream of one venue — rest:ticker, ws:book. ws:conn is the shared socket itself, which is where a connection-scoped fault is recorded.

matches ^(rest|ws):[a-z]+$ · e.g. "rest:ticker"

component.kindstring

"rest""ws"

component.labelstring
component.targetstring | null
detectionDetection
officialobject
official.sourcestring | null
official.pagestring | null
official.observedboolean
relatedIncident[]