Skip to content

Errors & status codes

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

Last updated

The API uses standard HTTP status codes and one stable JSON envelope. Every endpoint follows the rules below.

Envelope

Errors always carry an error field:

json
{ "error": "Descriptive error message" }

Some errors add structured fields — the missing-scope 403 and the quota 429s. Treat unrecognised extras as forward-compatible additions.

Success uses the data envelope:

json
{ "data": { ... } }

Collections nest results in data and add a top-level pagination object. See Monitors API.

Status codes

StatusMeaning
400 Bad RequestA query or path parameter is malformed. The message names the offending field.
401 UnauthorizedMissing, malformed, unknown, revoked or expired key.
403 ForbiddenAuthenticated but not allowed: missing scope, IP not on the allowlist, or organization not active.
404 Not FoundNo such resource in the calling organization.
429 Too Many RequestsPer-key rate limit or per-organization quota exceeded.
500 Internal Server ErrorUnhandled server-side error. Retry with backoff.
503 Service UnavailableA backend dependency is degraded. Retry shortly.

There is no 422 — validation failures return 400.

400 — validation

json
{ "error": "Invalid query parameter: per_page must be between 1 and 100" }

The first failing field is reported. Fix it and resubmit.

401 — authentication

json
{ "error": "Invalid API key" }
{ "error": "API key revoked" }
{ "error": "API key expired" }

Invalid API key covers "no header", "malformed" and "unknown" alike — the cases are not distinguished on the wire so a valid prefix cannot be probed. When debugging, check the key's status in the dashboard first.

403 — forbidden

IP allowlist mismatch:

json
{ "error": "IP not allowed for this API key" }

Organization deactivated:

json
{ "error": "Organization is not active" }

Missing scope:

json
{
  "error": "Missing required scope",
  "required_scope": "monitors:read",
  "granted_scopes": ["account:read"]
}

Or, when several scopes would satisfy the endpoint:

json
{
  "error": "Missing required scope",
  "required_scopes_any_of": ["monitors:read", "monitors:write"],
  "granted_scopes": ["account:read"]
}

See Scopes.

404 — not found

json
{ "error": "Monitor not found" }
Foreign resources return 404, not 403

The API does not distinguish "this does not exist" from "this exists but is not yours". Both are 404, which prevents enumerating UUIDs you do not own.

429 — rate limit or quota

Rate limit, per key per minute:

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

Quota, per organization:

json
{
  "error": "Daily API quota exceeded",
  "limit": 100000,
  "resets_at": "2026-08-06T00:00:00.000Z"
}

They need different handling — see Rate limits & quotas.

500 — server error

json
{ "error": "Internal server error" }

Safe to retry with exponential backoff. Production responses never include server-side detail. If it reproduces, send support the endpoint and the timestamp.

503 — service unavailable

json
{ "status": "degraded", "db": "down" }

Returned by /readyz when a backend dependency is failing. Back off 30–60 seconds. If it persists, check StatusOwl's own status page.

Idempotency

The whole surface is GET, so it is idempotent by construction. There is no idempotency-key support, because there is nothing to make idempotent.

See also