Skip to content

Errors & status codes

The error envelope, every status code the StatusOwl API can return, and what each one means.

Last updated May 9, 2026

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:

text
{ "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:

text
{ "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

StatusMeaning
400 Bad RequestValidation error — query params, path params, or request body are malformed. The error message identifies the offending field.
401 UnauthorizedMissing, malformed, unknown, revoked, or expired API key.
403 ForbiddenAuthenticated, but the request is not allowed: missing scope, IP not on the key's allowlist, or the organization is not active.
404 Not FoundResource doesn't exist, or it belongs to a different organization (the API does not distinguish — see below).
429 Too Many RequestsPer-key rate limit or per-org daily / monthly quota exceeded.
500 Internal Server ErrorUnhandled server-side error. Safe to retry with backoff; if persistent, contact support with the timestamp.
503 Service UnavailableA 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:

text
{ "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:

text
{ "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:

text
{ "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:

text
{ "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:

text
{
  "error": "Missing required scope",
  "required_scope": "monitors:write",
  "granted_scopes": ["monitors:read"]
}

Or, when the endpoint accepts any of several scopes:

text
{
  "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

text
{ "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):

text
{
  "error": "Rate limit exceeded",
  "limit_rpm": 600,
  "retry_after_seconds": 42
}

Plus a Retry-After: 42 header.

Daily or monthly quota (per organization):

text
{
  "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

text
{ "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

text
{ "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