Status page API
The status page has its own API, separate from your account's. It runs on its own domain and its own database, so it keeps answering when the rest of the platform does not.
| The status API | Detail |
|---|---|
| Base URL | https://status-api.freshperf.fr/api/v1 |
| Authentication | none. No key, no header |
| Methods | GET only |
| Reach | the platform's public state, never your services or your account |
| Detailed reference | status.freshperf.fr/en-us/api, with a try-it console |
The account API's permissions, its {"data": ...} envelope, its cursors and its error markers do not apply here. This API has its own, shorter conventions.
Conventions
- Translated fields: always both languages,
{"en": "...", "fr": "..."}. There is no language parameter. - Timestamps: epoch milliseconds, UTC.
- Errors: a marker alone,
{"error": "NOT_FOUND"}or{"error": "VALIDATION_FAILED", "field": "range"}. Statuses400and404. - No envelope: the response is the object itself, with no
dataaround it. - Cache: the summary is computed at most every 30 seconds server-side. Polling faster returns the same content.
- CORS: only the status page's own origins are allowed. From a server, CORS does not apply.
A rate limit guards against bursts of failing calls; a client getting 200 responses does not meet it. Polling every 30 to 60 seconds is enough.
GET /status
The shortest summary. This is what a badge or a probe needs.
curl https://status-api.freshperf.fr/api/v1/status
{ "overall": "operational", "updatedAt": 1787921863099, "servicesUp": 5, "servicesDown": 0, "activeIncidents": 0, "activeMaintenances": 1 }
| Field | Type | Notes |
|---|---|---|
overall | string | operational, degraded_performance, partial_outage, major_outage, maintenance |
updatedAt | integer | epoch milliseconds |
servicesUp, servicesDown | integer | monitored services in each state |
activeIncidents, activeMaintenances | integer | counts. The detail is in /summary |
overall is derived in this order: at least one service down gives partial_outage, or major_outage when half or more are; otherwise the worst active incident decides; otherwise a maintenance in progress gives maintenance; otherwise operational.
GET /summary
Everything the home page shows, in one call.
{ "overall": "operational", "updatedAt": 1787921863099, "servicesUp": 5, "servicesDown": 0, "groups": [ { "slug": "web", "name": { "en": "Websites", "fr": "Sites" }, "description": null, "services": [ { "slug": "freshperf", "name": { "en": "freshperf.fr", "fr": "freshperf.fr" }, "description": null, "status": "up", "lastCheckAt": 1787921836639, "latencyMs": 164, "uptime90d": 99.956, "days": [ { "date": "2026-05-31", "uptimePct": 100.0, "state": "ok" } ] } ] } ], "activeIncidents": [], "maintenance": [] }
| Service field | Type | Notes |
|---|---|---|
slug | string | the identifier to pass to /services/{slug}/metrics |
status | string | up, down, unknown |
lastCheckAt | integer or null | epoch milliseconds of the last probe |
latencyMs | integer or null | latency of the last probe |
uptime90d | number or null | 90-day uptime, weighted by the number of probes |
days[] | array | one object per day, oldest first |
days[].date | string | YYYY-MM-DD, UTC |
days[].uptimePct | number or null | null when that day has no probe |
days[].state | string | ok (≥ 99.5%), degraded (≥ 95%), down, no_data |
activeIncidents holds the same objects as /incidents, maintenance the same as /maintenance. Disabled groups and services are never listed.
GET /services/{slug}/metrics
| Parameter | Type | Required | Values | Where it comes from |
|---|---|---|---|---|
slug | path | yes | - | /summary → groups[].services[].slug |
range | query | no, default 24h | 24h, 7d, 90d | - |
curl "https://status-api.freshperf.fr/api/v1/services/freshperf/metrics?range=7d"
{ "slug": "freshperf", "range": "7d", "points": [ { "t": 1787835687488, "latencyMs": 149, "uptimePct": 100.0 } ] }
range | Source | Points |
|---|---|---|
24h, 7d | the raw probes, grouped into buckets | 200 at most, buckets of at least a minute |
90d | the daily rollups | one per day, up to 90 |
t is the middle of the bucket, in epoch milliseconds. latencyMs and uptimePct are null when the bucket holds no usable probe.
| Error | Status |
|---|---|
{"error": "VALIDATION_FAILED", "field": "range"} | 400 |
{"error": "NOT_FOUND"} | 404, unknown slug or disabled service |
GET /incidents
| Parameter | Type | Required | Values |
|---|---|---|---|
state | query | no, default all | all, active, resolved |
page | query | no, default 0 | page index, zero-based |
pageSize | query | no, default 10 | 1 to 50 |
{ "items": [ { "id": 12, "severity": "major", "status": "resolved", "title": { "en": "Elevated error rate", "fr": "Taux d'erreurs élevé" }, "affectedServiceIds": [3], "startedAt": 1787440000000, "resolvedAt": 1787452000000 } ], "page": 0, "pageSize": 10, "total": 1 }
| Field | Type | Notes |
|---|---|---|
severity | string | minor, major, critical |
status | string | investigating, identified, monitoring, resolved |
affectedServiceIds | array of integers | internal ids of the services hit. These are not the slug values |
resolvedAt | integer or null | null while the incident is open |
With state=active the list is not paginated: page and pageSize are ignored, and total is the number of incidents returned.
GET /incidents/{id}
One incident and its full timeline, newest update first.
{ "id": 12, "severity": "major", "status": "resolved", "title": { "en": "Elevated error rate", "fr": "Taux d'erreurs élevé" }, "affectedServiceIds": [3], "startedAt": 1787440000000, "resolvedAt": 1787452000000, "updates": [ { "status": "resolved", "body": { "en": "Incident resolved.", "fr": "Incident résolu." }, "createdAt": 1787452000000 } ] }
Unknown id: 404 {"error": "NOT_FOUND"}.
GET /maintenance
| Parameter | Type | Required | Values |
|---|---|---|---|
window | query | no, default upcoming | upcoming, past, all |
page | query | no, default 0 | page index, zero-based. Honoured with past only |
pageSize | query | no, default 10 | 1 to 50. Same rule |
{ "items": [ { "id": 4, "title": { "en": "Database upgrade", "fr": "Mise à niveau base de données" }, "body": { "en": "Planned upgrade.", "fr": "Mise à niveau planifiée." }, "scheduledStart": 1787620000000, "scheduledEnd": 1787627200000, "affectedServiceIds": [1, 3], "state": "upcoming" } ], "window": "upcoming" }
The response carries no page, pageSize or total.
window | Contents | Order |
|---|---|---|
upcoming | windows that have not ended, cancelled ones excluded | start ascending |
past | windows that have ended | start descending, paginated |
all | all of them, cancelled included | start descending |
state is derived from the current time: cancelled, then upcoming, in_progress or completed.
Going further
status.freshperf.fr/en-us/api renders the same reference with a console to run each call from the page.