Errors & status codes
The error envelope, every status code the StatusOwl API can return, and what each one means.
The StatusOwl REST API uses standard HTTP status codes and a single, stable JSON envelope for errors. This page is the canonical reference for both — keep it open while integrating; every endpoint follows the rules below.
Error envelope
Every error response carries a JSON body with at minimum an error field:
{ "error": "Descriptive error message" }
Some error responses include extra structured fields — for example, the 403
"missing scope" body and the 429 quota bodies. Those extras are documented on
the relevant pages
(scopes,
rate limits) and listed below per status
code. The error field is always present. Treat unknown extras as
forward-compatible additions.
Successful responses use the data envelope instead:
{ "data": { ... } }
Read endpoints that return collections nest their results in data and add a
pagination object at the top level. See the
Monitors API for an example.
Status code reference
| Status | Meaning |
|---|---|
| 400 Bad Request | Validation error — query params, path params, or request body are malformed. The error message identifies the offending field. |
| 401 Unauthorized | Missing, malformed, unknown, revoked, or expired API key. |
| 403 Forbidden | Authenticated, but the request is not allowed: missing scope, IP not on the key's allowlist, or the organization is not active. |
| 404 Not Found | Resource doesn't exist, or it belongs to a different organization (the API does not distinguish — see below). |
| 429 Too Many Requests | Per-key rate limit or per-org daily / monthly quota exceeded. |
| 500 Internal Server Error | Unhandled server-side error. Safe to retry with backoff; if persistent, contact support with the timestamp. |
| 503 Service Unavailable | A backend dependency is degraded (returned by /readyz). Retry shortly. |
The API does not use 422 — validation errors return 400. If you've seen 422 in another StatusOwl API, you're looking at an internal endpoint, not the public REST API.
400 — validation
Validation failures from query parameters, path parameters, and JSON bodies
all map to 400. The error field describes the problem in human-readable form:
{ "error": "Invalid query parameter: per_page must be between 1 and 100" }
The first failing field is reported. If multiple fields are invalid, fix the reported one and resubmit.
401 — authentication
A 401 means the request couldn't be authenticated. Possible bodies:
{ "error": "Invalid API key" }
{ "error": "API key revoked" }
{ "error": "API key expired" }
The generic Invalid API key body is returned for "no Authorization header",
"malformed key", and "unknown key" alike — we don't distinguish those cases on
the wire so attackers can't probe for valid prefixes. If you're debugging an
unexpected 401, open the dashboard and check the key's status (active /
revoked / expired) before assuming a transport problem.
403 — forbidden
A 403 means the key authenticated, but the request isn't allowed. Three distinct shapes:
IP allowlist mismatch:
{ "error": "IP not allowed for this API key" }
The key has an IP allowlist configured and the request came from a different source. See IP allowlist.
Organization deactivated:
{ "error": "Organization is not active" }
The org has been deactivated (billing issue, policy violation, voluntary suspension). Contact support.
Missing required scope — structured body with the missing scope and the key's actual scopes for triage:
{
"error": "Missing required scope",
"required_scope": "monitors:write",
"granted_scopes": ["monitors:read"]
}
Or, when the endpoint accepts any of several scopes:
{
"error": "Missing required scope",
"required_scopes_any_of": ["monitors:read", "monitors:write"],
"granted_scopes": ["account:read"]
}
See Scopes & plan gating for the full catalog.
404 — not found
{ "error": "Not found" }
Resource-specific endpoints may use a more descriptive message
(e.g. "Monitor not found").
Foreign UUIDs return 404, not 403
The API does not distinguish "this resource doesn't exist" from "this resource exists but belongs to another organization". Both return 404. This prevents enumeration attacks against UUIDs you don't own.
429 — rate limit or quota
The 429 body shape depends on which gate you hit:
Rate limit (per minute, per key):
{
"error": "Rate limit exceeded",
"limit_rpm": 600,
"retry_after_seconds": 42
}
Plus a Retry-After: 42 header.
Daily or monthly quota (per organization):
{
"error": "Daily API quota exceeded",
"limit": 100000,
"resets_at": "2026-05-10T00:00:00.000Z"
}
Full handling guidance is on the Rate limits & quotas page.
500 — server error
{ "error": "Internal server error" }
Unhandled exception on our side. Safe to retry with exponential backoff. In
non-production environments the body may include a message field with the
internal error detail; production responses never leak server-side detail.
If a 500 reproduces, contact support with the request timestamp and the endpoint — we keep server-side traces and can pinpoint what went wrong.
503 — service unavailable
{ "status": "degraded", "db": "down" }
Returned by the /readyz endpoint when a backend dependency is failing. A
503 from a v1 endpoint indicates a transient backend problem — back off
30–60 seconds and retry. If a 503 persists past a few minutes, check
status.statusowl.net for an open incident.
Idempotency
The current v1 read-only surface is idempotent by nature — GET requests
have no side effects. There is no idempotency-key support for write
endpoints yet; when write endpoints ship, idempotency-key handling will be
documented here.
See also
- API authentication — bearer tokens, scopes, the request flow.
- Rate limits & quotas — full 429 reference and backoff strategy.
- Scopes & plan gating — the 403 missing-scope body and per-plan availability.
- Monitors API — example endpoint with full request / response shapes.