---
title: "API Reference"
description: "Reference for the UptimeProject public API. Every endpoint, parameter, response and schema, generated from the OpenAPI 3.1 document."
url: https://uptimeproject.org/docs/api/
built: 2026-11-06T12:58:15.145Z
---

# API reference

Read-only API for cloud provider uptime rankings and historical availability data. Backed by an independent fleet of probes measuring real cloud services on a 1-minute cadence; aggregated into per-minute and per-day time-series rollups and served as cacheable JSON.

All `/v1/*` GET responses are stamped with `Cache-Control: public, max-age=300, s-maxage=300` (5 minutes) by default. The OpenAPI spec endpoint uses a longer 1-hour TTL because the spec only changes on releases.

Errors use a single envelope: `{"error": "<human-readable string>"}`. The error string is for humans; clients should branch on the HTTP status code. `503` means the data store is unreachable and `504` means a query timed out; both are transient and safe to retry.

Availability percentages are minute-weighted: `100 * up minutes / measured minutes`, where measured minutes are those with consensus `up`, `down` or `degraded`. Minutes with consensus `unknown` are excluded from the denominator. Every percentage is truncated (not rounded) to 2 decimal places, so `100` only ever means every measured minute was up. A window with no measured minutes reports `null`, never `0`.

Each minute's consensus is classified under a versioned methodology. `/v1/methodology` lists every version and the date it took effect; uptime buckets report the version that classified them.

Base URL

https://api.uptimeproject.org

Version

v1

Authentication

None

Specification

[openapi.json](https://uptimeproject.org/openapi.json)

## services

Per-service metadata, current status, and headline availability.

GET `/v1/services`

### List active services

Returns every service registered as `active = true` in the registry. Each row carries the service's identity, headline availability over the last 30 and 90 days, and current consensus status. Intended for building a directory of services, or for taking a full snapshot of the catalog in one request.

Availability values are minute-weighted (see the API description); a `null` value means "no measurements in the window yet".

#### Responses

Responses for GET /v1/services
| Status | Body | Description |
| --- | --- | --- |
| 200 | [array of ServiceSummary](#schema-ServiceSummary) | A non-empty array of services. Order is unspecified. |
| 500 | [APIError](#schema-APIError) | Unexpected server-side error. Body is the standard error envelope; the `error` string is intentionally generic and clients should not try to parse it for diagnostics. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services
```

#### Response

            `[   {     "id": "aws-s3-us-east-1",     "name": "AWS S3",     "category": "object-storage",     "region": "us-east-1",     "provider": "Amazon Web Services",     "show_on_leaderboard": true,     "homepage_url": "https://aws.amazon.com/s3/",     "status_page_url": "https://status.aws.amazon.com/rss/s3-us-east-1.rss",     "current_status": "up",     "checks_count": 3,     "availability_24h": 99.99,     "availability_7d": 99.98,     "availability_30d": 99.97,     "availability_90d": 99.91   } ]`

GET `/v1/services/{id}`

### Fetch a single service by id

Returns the same fields as `/v1/services` for one service, plus per-window minute counts (`period_stats`), the active checks that produce its measurements and each probe's most-recent view of the service. Deactivated services and unknown ids both return 404: the API does not surface "this service used to exist" as a distinct state.

#### Parameters

Parameters for GET /v1/services/{id}
| Name | In | Type | Description |
| --- | --- | --- | --- |
| id required | path | string | Slug-style service identifier from the registry, e.g. `aws-s3-us-east-1`, `cloudflare-cdn`. Lower-case and dash-separated, and stable across releases. Regional services carry their region in the id, so the identifier for a service is not the provider's product name. |

#### Responses

Responses for GET /v1/services/{id}
| Status | Body | Description |
| --- | --- | --- |
| 200 | [ServiceDetail](#schema-ServiceDetail) | Service exists and is active. |
| 404 | [APIError](#schema-APIError) | The service id is not registered, or the service is deactivated. The two cases collapse to the same response; the API does not expose deactivated-vs-never-existed. |
| 500 | [APIError](#schema-APIError) | Unexpected server-side error. Body is the standard error envelope; the `error` string is intentionally generic and clients should not try to parse it for diagnostics. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services/aws-s3-us-east-1
```

#### Response

            `{   "id": "aws-s3-us-east-1",   "name": "AWS S3",   "category": "object-storage",   "region": "us-east-1",   "provider": "Amazon Web Services",   "show_on_leaderboard": true,   "homepage_url": "https://aws.amazon.com/s3/",   "status_page_url": "https://status.aws.amazon.com/rss/s3-us-east-1.rss",   "current_status": "up",   "checks_count": 3,   "availability_24h": 99.99,   "availability_7d": 99.98,   "availability_30d": 99.97,   "availability_90d": 99.91,   "period_stats": {     "24h": {       "availability_pct": 99.97,       "downtime_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0     },     "7d": {       "availability_pct": 99.97,       "downtime_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0     },     "30d": {       "availability_pct": 99.97,       "downtime_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0     },     "90d": {       "availability_pct": 99.97,       "downtime_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0     }   },   "checks": [     {       "id": "aws-s3-us-east-1-http-main",       "check_type": "http",       "target": "https://s3.us-east-1.amazonaws.com/",       "interval_sec": 60     }   ],   "probes": [     {       "probe_id": "hzr-fsn1",       "status": "up",       "latency_ms": 42     }   ] }`

## uptime

Time-series availability data for charts and timelines.

GET `/v1/services/{id}/uptime`

### Time-series availability for one service

Returns availability data over the requested period as an ordered series of buckets plus a rolled-up summary. Bucket size is chosen by the period:

| period | bucket size | points |
| --- | --- | --- |
| `24h` | 5 minutes | 289 |
| `7d` | 1 hour | 169 |
| `30d` | 1 day | 30 |
| `90d` | 1 day | 90 |
| `1y` | 1 day | 365 |

The `24h` and `7d` windows are inclusive of both ends, which is where the extra point over 288 and 168 comes from. Counts assume a full window of history; a service registered more recently returns fewer points.

The summary's `availability_pct` is computed across the whole period, not as an average of per-bucket percentages, so partial buckets at the leading edge don't get equal weight to full ones.

Latency fields use `http` checks only, so TLS handshakes, DNS lookups and storage operations do not blend into one number.

#### Parameters

Parameters for GET /v1/services/{id}/uptime
| Name | In | Type | Description |
| --- | --- | --- | --- |
| id required | path | string | Slug-style service identifier from the registry, e.g. `aws-s3-us-east-1`, `cloudflare-cdn`. Lower-case and dash-separated, and stable across releases. Regional services carry their region in the id, so the identifier for a service is not the provider's product name. |
| period | query | [UptimePeriod (enum)](#schema-UptimePeriod) | Time window to return. Defaults to `30d`. |

#### Responses

Responses for GET /v1/services/{id}/uptime
| Status | Body | Description |
| --- | --- | --- |
| 200 | [UptimeSeries](#schema-UptimeSeries) | Service exists and the requested period is valid. |
| 400 | [APIError](#schema-APIError) | The `period` query parameter is not one of the supported values. |
| 404 | [APIError](#schema-APIError) | The service id is not registered, or the service is deactivated. The two cases collapse to the same response; the API does not expose deactivated-vs-never-existed. |
| 500 | [APIError](#schema-APIError) | Unexpected server-side error. Body is the standard error envelope; the `error` string is intentionally generic and clients should not try to parse it for diagnostics. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services/aws-s3-us-east-1/uptime
```

#### Response

            `{   "service_id": "aws-s3-us-east-1",   "period": "24h",   "points": [     {       "bucket": "2026-05-08T09:00:00Z",       "availability_pct": 99.86,       "status": "up",       "down_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0,       "latency_p50_ms": 87,       "methodology_version": 2     }   ],   "summary": {     "availability_pct": 99.97,     "total_down_minutes": 0,     "total_degraded_minutes": 0,     "total_unknown_minutes": 0,     "total_measured_minutes": 0,     "latency_p50_ms": 142,     "methodology_versions": [       2     ]   } }`

## leaderboard

Ranked best-of lists by category and time window.

GET `/v1/leaderboard`

### Ranked best-of list of active services

Returns active services ranked by minute-weighted availability over the requested window.

Ranking uses standard competition ranking on the published 2-decimal `availability_pct`: services showing the same value share a rank, and the next rank skips (1, 1, 3). Within a tie, entries are listed by name ascending; the order does not imply a ranking.

A service with no measurements in the window reports `availability_pct: null` and ranks after every measured service. All such services share the last rank. They stay in the list rather than disappearing, so a client can tell "no data yet" apart from "ranked last".

The `category` filter accepts any of the registry-defined category strings. An unknown value returns 400 rather than an empty list, which would mask a typo.

#### Parameters

Parameters for GET /v1/leaderboard
| Name | In | Type | Description |
| --- | --- | --- | --- |
| period | query | [LeaderboardPeriod (enum)](#schema-LeaderboardPeriod) | Window over which availability is computed. Defaults to `30d`. Narrower than the set `/v1/services/{id}/uptime` accepts; see `LeaderboardPeriod` for which values are excluded and why. |
| category | query | [Category (enum)](#schema-Category) | Optional category filter. When omitted, all active services rank together; when present, only services in the matching category are ranked. |

#### Responses

Responses for GET /v1/leaderboard
| Status | Body | Description |
| --- | --- | --- |
| 200 | [LeaderboardResponse](#schema-LeaderboardResponse) | Ranked entries (possibly empty after a category filter). |
| 400 | [APIError](#schema-APIError) | `period` is not in the leaderboard whitelist, or `category` is non-empty and not a known category. |
| 500 | [APIError](#schema-APIError) | Unexpected server-side error. Body is the standard error envelope; the `error` string is intentionally generic and clients should not try to parse it for diagnostics. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/leaderboard
```

#### Response

            `{   "period": "7d",   "category": "object-storage",   "entries": [     {       "rank": 1,       "service_id": "aws-s3-us-east-1",       "name": "AWS S3",       "category": "object-storage",       "availability_pct": 99.97,       "downtime_minutes": 0,       "degraded_minutes": 0,       "unknown_minutes": 0,       "measured_minutes": 0,       "current_status": "up",       "rank_change": 0     }   ],   "generated_at": "2026-05-08T09:00:00Z" }`

## incidents

Outage and degradation episodes derived from probe consensus.

GET `/v1/incidents`

### Recent incidents across services

Returns recent incidents for every service shown on the leaderboard, newest first.

An incident is a run of minutes whose probe consensus was `down` or `degraded`. Runs separated by at most 2 minutes of any other status merge into one incident. Runs shorter than 2 minutes are not reported. Incidents are computed from the last 90 days of data only.

An incident is ongoing (`ended_at: null`) when its last bad minute is within the last 5 minutes and no `up` minute has followed it.

#### Parameters

Parameters for GET /v1/incidents
| Name | In | Type | Description |
| --- | --- | --- | --- |
| limit | query | integer | Maximum number of incidents to return. |
| since | query | string (date-time) | RFC 3339 timestamp. Only incidents still in progress at or after this time are returned. Values older than 90 days ago, and an omitted value, mean 90 days ago. The effective value is echoed as `since` in the JSON response. |

#### Responses

Responses for GET /v1/incidents
| Status | Body | Description |
| --- | --- | --- |
| 200 | [IncidentsResponse](#schema-IncidentsResponse) | Incidents, newest first (possibly empty). |
| 400 | [APIError](#schema-APIError) | `limit` is not an integer in 1..200, or `since` is not an RFC 3339 timestamp. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/incidents
```

#### Response

            `{   "since": "2026-07-25T09:00:00Z",   "incidents": [     {       "service_id": "aws-s3-us-east-1",       "service_name": "AWS S3",       "started_at": "2026-10-20T14:03:00Z",       "ended_at": "2026-10-20T14:21:00Z",       "duration_minutes": 18,       "down_minutes": 0,       "degraded_minutes": 0,       "worst_status": "down"     }   ],   "generated_at": "2026-10-23T09:00:00Z" }`

GET `/v1/incidents.atom`

### Recent incidents across services, as an Atom feed

The incidents of `/v1/incidents` as an Atom 1.0 feed, one entry per incident. An entry keeps its `id` for the life of the incident; its `updated` time is the end of the incident, or the response time while it is ongoing. Entries link to the service's page on uptimeproject.org.

#### Parameters

Parameters for GET /v1/incidents.atom
| Name | In | Type | Description |
| --- | --- | --- | --- |
| limit | query | integer | Maximum number of incidents to return. |
| since | query | string (date-time) | RFC 3339 timestamp. Only incidents still in progress at or after this time are returned. Values older than 90 days ago, and an omitted value, mean 90 days ago. The effective value is echoed as `since` in the JSON response. |

#### Responses

Responses for GET /v1/incidents.atom
| Status | Body | Description |
| --- | --- | --- |
| 200 | none | Atom 1.0 feed. |
| 400 | [APIError](#schema-APIError) | `limit` is not an integer in 1..200, or `since` is not an RFC 3339 timestamp. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/incidents.atom
```

GET `/v1/services/{id}/incidents`

### Recent incidents for one service

Same as `/v1/incidents`, restricted to one service. Includes services hidden from the leaderboard.

#### Parameters

Parameters for GET /v1/services/{id}/incidents
| Name | In | Type | Description |
| --- | --- | --- | --- |
| id required | path | string | Slug-style service identifier from the registry, e.g. `aws-s3-us-east-1`, `cloudflare-cdn`. Lower-case and dash-separated, and stable across releases. Regional services carry their region in the id, so the identifier for a service is not the provider's product name. |
| limit | query | integer | Maximum number of incidents to return. |
| since | query | string (date-time) | RFC 3339 timestamp. Only incidents still in progress at or after this time are returned. Values older than 90 days ago, and an omitted value, mean 90 days ago. The effective value is echoed as `since` in the JSON response. |

#### Responses

Responses for GET /v1/services/{id}/incidents
| Status | Body | Description |
| --- | --- | --- |
| 200 | [IncidentsResponse](#schema-IncidentsResponse) | Incidents, newest first (possibly empty). |
| 400 | [APIError](#schema-APIError) | `limit` is not an integer in 1..200, or `since` is not an RFC 3339 timestamp. |
| 404 | [APIError](#schema-APIError) | The service id is not registered, or the service is deactivated. The two cases collapse to the same response; the API does not expose deactivated-vs-never-existed. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services/aws-s3-us-east-1/incidents
```

#### Response

            `{   "since": "2026-07-25T09:00:00Z",   "incidents": [     {       "service_id": "aws-s3-us-east-1",       "service_name": "AWS S3",       "started_at": "2026-10-20T14:03:00Z",       "ended_at": "2026-10-20T14:21:00Z",       "duration_minutes": 18,       "down_minutes": 0,       "degraded_minutes": 0,       "worst_status": "down"     }   ],   "generated_at": "2026-10-23T09:00:00Z" }`

GET `/v1/services/{id}/incidents.atom`

### Recent incidents for one service, as an Atom feed

The incidents of `/v1/services/{id}/incidents` as an Atom 1.0 feed. Entry semantics match `/v1/incidents.atom`.

#### Parameters

Parameters for GET /v1/services/{id}/incidents.atom
| Name | In | Type | Description |
| --- | --- | --- | --- |
| id required | path | string | Slug-style service identifier from the registry, e.g. `aws-s3-us-east-1`, `cloudflare-cdn`. Lower-case and dash-separated, and stable across releases. Regional services carry their region in the id, so the identifier for a service is not the provider's product name. |
| limit | query | integer | Maximum number of incidents to return. |
| since | query | string (date-time) | RFC 3339 timestamp. Only incidents still in progress at or after this time are returned. Values older than 90 days ago, and an omitted value, mean 90 days ago. The effective value is echoed as `since` in the JSON response. |

#### Responses

Responses for GET /v1/services/{id}/incidents.atom
| Status | Body | Description |
| --- | --- | --- |
| 200 | none | Atom 1.0 feed. |
| 400 | [APIError](#schema-APIError) | `limit` is not an integer in 1..200, or `since` is not an RFC 3339 timestamp. |
| 404 | [APIError](#schema-APIError) | The service id is not registered, or the service is deactivated. The two cases collapse to the same response; the API does not expose deactivated-vs-never-existed. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services/aws-s3-us-east-1/incidents.atom
```

## badges

Embeddable SVG status badges.

GET `/v1/services/{id}/badge.svg`

### Availability badge for one service

A flat, shields-style SVG reading `uptime <period>: <availability>%`, for embedding in a README or status page. The value uses the same figure as the matching `availability_*` field of `/v1/services/{id}`. A window with no measurements reads `no data`.

Colour bands: at least 99.9 green, at least 99 yellow-green, at least 95 yellow, below 95 red, no data grey.

Error responses are JSON, not SVG.

#### Parameters

Parameters for GET /v1/services/{id}/badge.svg
| Name | In | Type | Description |
| --- | --- | --- | --- |
| id required | path | string | Slug-style service identifier from the registry, e.g. `aws-s3-us-east-1`, `cloudflare-cdn`. Lower-case and dash-separated, and stable across releases. Regional services carry their region in the id, so the identifier for a service is not the provider's product name. |
| period | query | enum | Window to report. Defaults to `30d`. One of: `24h` , `7d` , `30d` , `90d` |

#### Responses

Responses for GET /v1/services/{id}/badge.svg
| Status | Body | Description |
| --- | --- | --- |
| 200 | none | SVG badge. |
| 400 | [APIError](#schema-APIError) | The `period` query parameter is not one of the supported values. |
| 404 | [APIError](#schema-APIError) | The service id is not registered, or the service is deactivated. The two cases collapse to the same response; the API does not expose deactivated-vs-never-existed. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/services/aws-s3-us-east-1/badge.svg
```

## meta

Machine-readable spec, the docs redirect and the consensus methodology.

GET `/v1/methodology`

### Consensus methodology versions

Lists every version of the consensus methodology, the rule set that turns per-probe results into each minute's `up`, `down`, `degraded` or `unknown` status, with the time each took effect. A version applies to minutes from its `effective_from` until the next version's.

#### Responses

Responses for GET /v1/methodology
| Status | Body | Description |
| --- | --- | --- |
| 200 | [MethodologyResponse](#schema-MethodologyResponse) | Every methodology version, ascending. |
| 503 | [APIError](#schema-APIError) | The data store is unreachable. Transient; safe to retry. |
| 504 | [APIError](#schema-APIError) | The query did not finish in time. Transient; safe to retry. |

#### Request

```
curl https://api.uptimeproject.org/v1/methodology
```

#### Response

            `{   "current_version": 2,   "versions": [     {       "version": 2,       "effective_from": "2026-10-23T00:00:00Z",       "description": "string"     }   ] }`

GET `/v1/openapi.json`

### Machine-readable API specification

Returns this OpenAPI 3.1 document as JSON, for clients that generate code from OpenAPI (e.g. `openapi-typescript`).

Served with a 1-hour TTL, longer than the 5 minutes the data endpoints use, because the spec only changes on releases.

#### Responses

Responses for GET /v1/openapi.json
| Status | Body | Description |
| --- | --- | --- |
| 200 | object | The OpenAPI 3.1 document. |

#### Request

```
curl https://api.uptimeproject.org/v1/openapi.json
```

GET `/v1/docs`

### Redirect to the human-readable API reference

Returns a 302 redirect to [uptimeproject.org/docs/api](https://uptimeproject.org/docs/api/), the human-readable rendering of this document.

Exposed on the API host so a developer who lands on the raw base URL, or a CLI that follows redirects, reaches the readable view in one hop. That page is generated at build time from a copy of this document, so it stays readable while the API is unreachable.

#### Responses

Responses for GET /v1/docs
| Status | Body | Description |
| --- | --- | --- |
| 302 | none | Redirect to the marketing-site docs page. |

#### Request

```
curl https://api.uptimeproject.org/v1/docs
```

## Schemas

Response shapes referenced above. Fields marked required are always present; the rest may be absent.

### MethodologyVersion

One version of the consensus methodology.

Fields of the MethodologyVersion object
| Field | Type | Description |
| --- | --- | --- |
| version required | integer | Version number, increasing with each change. Example: `2` |
| effective\_from required | string (date-time) or null | First minute classified under this version, UTC, RFC 3339. `null` for a version with no start, which covers every minute before the next version. Example: `2026-10-23T00:00:00Z` |
| description required | string | Summary of the rules this version applies. |

### MethodologyResponse

Body of `GET /v1/methodology`.

Fields of the MethodologyResponse object
| Field | Type | Description |
| --- | --- | --- |
| current\_version required | integer or null | The version classifying minutes now: the highest version whose `effective_from` has passed. `null` only before any version has taken effect. Example: `2` |
| versions required | [array of MethodologyVersion](#schema-MethodologyVersion) | Every version, ascending. Always a JSON array. |

### Status

Probe-consensus status for a service or bucket.

Under methodology version 2, each primary check is judged on its own and the service takes the worst result:

-   `up`: a strict majority of voting probes succeeded on every primary check
-   `degraded`: on at least one primary check, some voting probes failed but not all, and no primary check is down
-   `down`: on at least one primary check, every voting probe failed
-   `unknown`: every primary check had fewer than 3 voting probes

TLS checks do not vote. Probes on the measured provider's own network do not vote on it, and a probe that fails 30% or more of at least 10 one-minute checks in a minute abstains for that minute.

The exact rules depend on the methodology version in force for the minute; see `/v1/methodology`.

### UptimePeriod

Period accepted by `/v1/services/{id}/uptime`. Bucket size is fixed per period (see endpoint description).

One of: `24h` , `7d` , `30d` , `90d` , `1y`

### LeaderboardPeriod

Period accepted by `/v1/leaderboard`. Subset of `UptimePeriod`: `24h` is excluded as too noisy for ranking (one bad hour would flip a service to last place); `1y` is excluded as too lagging (rewards last-year stability over current performance).

One of: `7d` , `30d` , `90d`

### Category

Service category. Mirrors the registry's category names verbatim, so the value is `object-storage` (hyphenated), not `storage`.

One of: `iaas` , `cdn` , `dns` , `object-storage` , `dev-infra` , `email` , `payments` , `ai`

### APIError

Canonical error envelope used by every non-2xx response. The `error` string is human-readable and may change between deploys, so clients should branch on the HTTP status.

Fields of the APIError object
| Field | Type | Description |
| --- | --- | --- |
| error required | string | Human-readable error message. Example: `not found` |

### ServiceSummary

One row of `/v1/services`, and the base that `ServiceDetail` extends. Carries identity, headline availability, current consensus status, and the number of active checks, so a client can summarise the whole catalog without fetching each service.

Fields of the ServiceSummary object
| Field | Type | Description |
| --- | --- | --- |
| id required | string | Slug-style identifier from the registry. Example: `aws-s3-us-east-1` |
| name required | string | Human-readable service name. Example: `AWS S3` |
| category required | [Category (enum)](#schema-Category) |  |
| region | string | Provider region for regional services. Omitted for global services (CDNs, DNS resolvers, payment APIs). Example: `us-east-1` |
| provider | string | Brand or parent company behind the service. Editorial rather than derived from `id` or `name`: every `aws-*` service reports "Amazon Web Services" whatever the product is called, and `linode` reports "Akamai / Linode" for the parent company. Omitted when the registry row carries no provider. Example: `Amazon Web Services` |
| show\_on\_leaderboard required | boolean | Whether the service appears on public ranking surfaces. `true`, the default, lists it on the leaderboard and the services index. `false` keeps it probed and queryable through `/v1/services/{id}` and `/v1/services/{id}/uptime` but off those rankings, which is how a newly onboarded service is held back until it has enough history to rank fairly. Always present, never omitted: an absent field would read as "the API does not know" rather than "hidden". Example: `true` |
| homepage\_url required | string (uri) | Public landing page for the service. Always present. Example: `https://aws.amazon.com/s3/` |
| status\_page\_url | string (uri) | The provider's own status page, where it publishes one. Omitted rather than empty when it does not, so a client never has to special-case `""`. Carried on the list endpoint as well as the detail one, so rendering a directory of services needs a single request. Example: `https://status.aws.amazon.com/rss/s3-us-east-1.rss` |
| current\_status required | [Status (enum)](#schema-Status) |  |
| checks\_count required | integer | Number of active checks measuring this service. Checks registered but not yet active are excluded, so the count matches what the fleet is really executing. `0` for a newly registered service whose checks have not landed yet. Example: `3` |
| availability\_24h required | number or null | Availability percent over the rolling trailing 24 hours, computed from per-minute consensus. `null` when the window has no measurements yet. Example: `99.99` |
| availability\_7d required | number or null | Availability percent over the trailing 7 days, computed from the per-day rollup, which is anchored to UTC midnight. `null` when the window has no measurements yet. Example: `99.98` |
| availability\_30d required | number or null | Same as `availability_7d`, over 30 days. Example: `99.97` |
| availability\_90d required | number or null | Same as `availability_7d`, over 90 days. Example: `99.91` |

### ServiceDetail

Body of `GET /v1/services/{id}`. Every field of `ServiceSummary`, plus the active checks that produce the measurements and each probe's most-recent view of the service.

Fields of the ServiceDetail object
| Field | Type | Description |
| --- | --- | --- |
| id required | string | Slug-style identifier from the registry. Example: `aws-s3-us-east-1` |
| name required | string | Human-readable service name. Example: `AWS S3` |
| category required | [Category (enum)](#schema-Category) |  |
| region | string | Provider region for regional services. Omitted for global services (CDNs, DNS resolvers, payment APIs). Example: `us-east-1` |
| provider | string | Brand or parent company behind the service. Editorial rather than derived from `id` or `name`: every `aws-*` service reports "Amazon Web Services" whatever the product is called, and `linode` reports "Akamai / Linode" for the parent company. Omitted when the registry row carries no provider. Example: `Amazon Web Services` |
| show\_on\_leaderboard required | boolean | Whether the service appears on public ranking surfaces. `true`, the default, lists it on the leaderboard and the services index. `false` keeps it probed and queryable through `/v1/services/{id}` and `/v1/services/{id}/uptime` but off those rankings, which is how a newly onboarded service is held back until it has enough history to rank fairly. Always present, never omitted: an absent field would read as "the API does not know" rather than "hidden". Example: `true` |
| homepage\_url required | string (uri) | Public landing page for the service. Always present. Example: `https://aws.amazon.com/s3/` |
| status\_page\_url | string (uri) | The provider's own status page, where it publishes one. Omitted rather than empty when it does not, so a client never has to special-case `""`. Carried on the list endpoint as well as the detail one, so rendering a directory of services needs a single request. Example: `https://status.aws.amazon.com/rss/s3-us-east-1.rss` |
| current\_status required | [Status (enum)](#schema-Status) |  |
| checks\_count required | integer | Number of active checks measuring this service. Checks registered but not yet active are excluded, so the count matches what the fleet is really executing. `0` for a newly registered service whose checks have not landed yet. Example: `3` |
| availability\_24h required | number or null | Availability percent over the rolling trailing 24 hours, computed from per-minute consensus. `null` when the window has no measurements yet. Example: `99.99` |
| availability\_7d required | number or null | Availability percent over the trailing 7 days, computed from the per-day rollup, which is anchored to UTC midnight. `null` when the window has no measurements yet. Example: `99.98` |
| availability\_30d required | number or null | Same as `availability_7d`, over 30 days. Example: `99.97` |
| availability\_90d required | number or null | Same as `availability_7d`, over 90 days. Example: `99.91` |
| period\_stats required | [ServicePeriodStats](#schema-ServicePeriodStats) |  |
| checks required | [array of CheckSummary](#schema-CheckSummary) | Active checks for this service. Always an array, never `null`, and empty for a newly registered service whose checks have not landed yet. |
| probes required | [array of ProbeStatus](#schema-ProbeStatus) | Each vantage point's current view of the service, taken from the most-recent minute that probe reported on, within a 5-minute lookback. A probe that has not reported inside that window is absent from the array rather than listed as unknown. Always an array, never `null`, and empty for a service with no recent measurements. |

### CheckSummary

One active check on a service. Published so that what is measured, and how, can be checked rather than taken on trust.

Fields of the CheckSummary object
| Field | Type | Description |
| --- | --- | --- |
| id required | string | Stable check identifier, formed from the service id, the check type and the target it covers. Example: `aws-s3-us-east-1-http-main` |
| check\_type required | string | Probe type. Currently one of `http`, `tls`, `storage`, `dns`. Typed as a string rather than an enum so adding a probe type stays a non-breaking change. Example: `http` |
| target required | string | What the probe hits: a URL, an IP and port, a hostname. The format depends on `check_type`. Where the real target embeds a private identifier, such as a storage bucket name, the API substitutes a `redacted` placeholder of the same shape (for example `https://redacted.s3.eu-west-1.amazonaws.com/probe.bin`). The check is still described, down to the operation and the region, without publishing the identifier. Only the public wire shape is substituted; the probe hits the real target. Example: `https://s3.us-east-1.amazonaws.com/` |
| interval\_sec required | integer | Probe cadence in seconds. Example: `60` |

### ProbeStatus

One vantage point's most-recent view of a service, returned inside `ServiceDetail.probes`.

Status uses the same vocabulary as the service-wide consensus (`up`, `down`, `degraded`) but is computed per probe, across the service's checks within the most-recent minute that probe reported on: `up` if every check succeeded, `down` if none did, `degraded` in between. There is no `unknown`, because a probe that has not reported inside the 5-minute lookback is absent from the array.

Fields of the ProbeStatus object
| Field | Type | Description |
| --- | --- | --- |
| probe\_id required | string | Stable probe identifier from the fleet registry, e.g. `hzr-fsn1`. Use it as the key when joining `probes` against probe metadata such as location and provider. Example: `hzr-fsn1` |
| status required | enum | Per-probe consensus across the service's checks within the most-recent reported minute. `unknown` is not valid here: a probe with no recent report is absent from the array entirely. One of: `up` , `down` , `degraded` Example: `up` |
| latency\_ms required | integer or null | p50 latency this probe observed in the most-recent minute, from the service's `http` checks only, taken as the maximum across them. `null` when that minute has no successful `http` measurement, which is a different case from the probe not reporting at all: that probe is absent from the array. Example: `42` |

### UptimePoint

One bucket on the uptime time series. Bucket size depends on the requested period (see `/v1/services/{id}/uptime`).

Fields of the UptimePoint object
| Field | Type | Description |
| --- | --- | --- |
| bucket required | string (date-time) | Timestamp at the start of the bucket, UTC, RFC 3339. Example: `2026-05-08T09:00:00Z` |
| availability\_pct required | number or null | Percent of measured minutes in the bucket that were classified `up`. `null` when the bucket has no measured minutes, in which case `status` is `unknown`. Example: `99.86` |
| status required | [Status (enum)](#schema-Status) | Worst minute in the bucket: `down` if any minute was down, else `degraded` if any was degraded, else `up`. `unknown` when no minute was measured. |
| down\_minutes required | integer | Count of minutes in the bucket with consensus `down`. |
| degraded\_minutes required | integer | Count of minutes in the bucket with consensus `degraded`. |
| unknown\_minutes required | integer | Count of minutes in the bucket with consensus `unknown`. |
| measured\_minutes required | integer | Count of minutes with consensus `up`, `down` or `degraded`: the denominator of `availability_pct`. |
| latency\_p50\_ms required | integer or null | Success-count-weighted p50 (median) response time, in milliseconds, across every `http` check and probe pair inside the bucket. `null` when the bucket contained no successful `http` measurement, so a chart should break the latency line there rather than plot a zero. Example: `87` |
| methodology\_version required | integer | Consensus methodology version that classified the bucket's minutes (see `/v1/methodology`). A bucket spanning a change of version reports the newer one. Example: `2` |

### UptimeSummary

Rolled-up totals for the whole period. `availability_pct` is count-weighted across every measured minute (NOT an average of per-bucket percentages) so partial buckets don't get equal weight to full ones.

Fields of the UptimeSummary object
| Field | Type | Description |
| --- | --- | --- |
| availability\_pct required | number or null | `100 * total_up / total_measured_minutes` across every measured minute in the period. `null` when no minute had measured data. Example: `99.97` |
| total\_down\_minutes required | integer | Sum of `down_minutes` across every point. |
| total\_degraded\_minutes required | integer | Sum of `degraded_minutes` across every point. |
| total\_unknown\_minutes required | integer | Sum of `unknown_minutes` across every point. |
| total\_measured\_minutes required | integer | Sum of `measured_minutes` across every point. |
| latency\_p50\_ms required | integer or null | Success-count-weighted p50 response time, in milliseconds, across every successful `http` measurement in the period. `null` when the period contained none. Useful as the headline "Average response time" for a service detail page. Example: `142` |
| methodology\_versions required | array of integer | Every consensus methodology version the period's points were classified under, ascending. More than one entry means the period spans a change of methodology. Empty when the period has no points. Example: `2` |

### UptimeSeries

Body of `GET /v1/services/{id}/uptime`.

Fields of the UptimeSeries object
| Field | Type | Description |
| --- | --- | --- |
| service\_id required | string | Echoed back from the path parameter. Example: `aws-s3-us-east-1` |
| period required | [UptimePeriod (enum)](#schema-UptimePeriod) |  |
| points required | [array of UptimePoint](#schema-UptimePoint) | Buckets in ascending bucket-time order. Always renders as a JSON array (never `null`); empty `[]` is valid for a service with no measurements yet. |
| summary required | [UptimeSummary](#schema-UptimeSummary) |  |

### PeriodStats

Minute-weighted availability aggregate for one window.

Fields of the PeriodStats object
| Field | Type | Description |
| --- | --- | --- |
| availability\_pct required | number or null | `100 * up minutes / measured_minutes`, truncated to 2 decimal places. `null` when `measured_minutes` is `0`. Example: `99.97` |
| downtime\_minutes required | integer | Minutes with consensus `down`. |
| degraded\_minutes required | integer | Minutes with consensus `degraded`. |
| unknown\_minutes required | integer | Minutes with consensus `unknown` (fewer than 3 probes reporting). Not part of `measured_minutes`. |
| measured\_minutes required | integer | Minutes with consensus `up`, `down` or `degraded`. |

### ServicePeriodStats

`PeriodStats` for each headline window. Each `availability_pct` equals the matching `availability_*` field of the service.

Fields of the ServicePeriodStats object
| Field | Type | Description |
| --- | --- | --- |
| 24h required | [PeriodStats](#schema-PeriodStats) |  |
| 7d required | [PeriodStats](#schema-PeriodStats) |  |
| 30d required | [PeriodStats](#schema-PeriodStats) |  |
| 90d required | [PeriodStats](#schema-PeriodStats) |  |

### Incident

A run of `down` or `degraded` consensus minutes for one service. See `/v1/incidents` for how runs are formed.

Fields of the Incident object
| Field | Type | Description |
| --- | --- | --- |
| service\_id required | string | Cross-references `/v1/services/{id}`. Example: `aws-s3-us-east-1` |
| service\_name required | string | Human-readable service name. Example: `AWS S3` |
| started\_at required | string (date-time) | Start of the first bad minute, UTC, RFC 3339. Example: `2026-10-20T14:03:00Z` |
| ended\_at required | string (date-time) or null | End of the last bad minute (exclusive), UTC, RFC 3339. `null` while the incident is ongoing. Example: `2026-10-20T14:21:00Z` |
| duration\_minutes required | integer | `ended_at - started_at` in minutes, or the span so far while ongoing. Includes merged gaps, so it can exceed `down_minutes + degraded_minutes`. Example: `18` |
| down\_minutes required | integer | Minutes in the incident with consensus `down`. |
| degraded\_minutes required | integer | Minutes in the incident with consensus `degraded`. |
| worst\_status required | enum | `down` if any minute was down, else `degraded`. One of: `down` , `degraded` |

### IncidentsResponse

Body of `GET /v1/incidents` and `GET /v1/services/{id}/incidents`.

Fields of the IncidentsResponse object
| Field | Type | Description |
| --- | --- | --- |
| since required | string (date-time) | Effective lower bound after applying the 90-day limit. Example: `2026-07-25T09:00:00Z` |
| incidents required | [array of Incident](#schema-Incident) | Newest first by `started_at`. Always a JSON array, never `null`. |
| generated\_at required | string (date-time) | Server wall-clock at response build time, UTC, RFC 3339. Example: `2026-10-23T09:00:00Z` |

### LeaderboardEntry

One ranked row in `/v1/leaderboard`.

Fields of the LeaderboardEntry object
| Field | Type | Description |
| --- | --- | --- |
| rank required | integer | 1-based standard competition rank on the published 2-decimal `availability_pct`. Equal values share a rank and the next rank skips (1, 1, 3). Services with `availability_pct: null` share the last rank. Clients should not recompute rank. |
| service\_id required | string | Slug-style identifier; cross-references `/v1/services/{id}`. Example: `aws-s3-us-east-1` |
| name required | string | Human-readable service name. Example: `AWS S3` |
| category required | [Category (enum)](#schema-Category) |  |
| availability\_pct required | number or null | Minute-weighted availability percent over the requested period, truncated to 2 decimal places. `null` when the service has no measurements in the period. Example: `99.97` |
| downtime\_minutes required | integer | Minutes with consensus `down` in the period. |
| degraded\_minutes required | integer | Minutes with consensus `degraded` in the period. |
| unknown\_minutes required | integer | Minutes with consensus `unknown` in the period. |
| measured\_minutes required | integer | Minutes with consensus `up`, `down` or `degraded`: the denominator of `availability_pct`. |
| current\_status required | [Status (enum)](#schema-Status) |  |
| rank\_change | integer or null | Rank movement against the previous publication, positive for an improvement and negative for a decline. Always absent in v1, because historical leaderboard state is not persisted yet. The field appears once it is. |

### LeaderboardResponse

Body of `GET /v1/leaderboard`.

Fields of the LeaderboardResponse object
| Field | Type | Description |
| --- | --- | --- |
| period required | [LeaderboardPeriod (enum)](#schema-LeaderboardPeriod) |  |
| category | [Category (enum)](#schema-Category) | Echoed when a `?category=` filter was applied; absent on the unfiltered call. |
| entries required | [array of LeaderboardEntry](#schema-LeaderboardEntry) | Ranked rows, ordered by `rank ASC` then `name ASC`. Always renders as a JSON array (never `null`); empty `[]` is valid after a category filter that matches no services. |
| generated\_at required | string (date-time) | Server wall-clock at response build time, UTC, RFC 3339. Useful for cache debugging, and for rendering an "as of" line alongside a ranking. Example: `2026-05-08T09:00:00Z` |
