Rate limits & errors

Rate limits

Every request — REST or MCP — is limited to 300 requests per minute, keyed to your personal access token rather than your IP address. The limit is shared across your whole token: REST calls and MCP tool calls draw from the same 300-per-minute budget, not separate ones.

Exceeding the limit returns 429 Too Many Requests in the same RFC 7807 shape every other error uses — see Rate limited below.

The check-trigger limit

POST /api/v1/monitors/{id}/check and the MCP trigger_check tool share a second, separate limit: one manual trigger per monitor per 60 seconds. This is keyed to the monitor, not your token — if you trigger a check from the dashboard, the REST API, and MCP in quick succession, only the first one goes through, regardless of which surface it came from.

A rejected trigger returns 429 with a Retry-After header set to the number of seconds until the lock clears.

Error responses

Every error response — from either surface — uses the same RFC 7807 Problem Details shape:

{
  "type": "https://api.stector.app/errors/<code>",
  "title": "<short title>",
  "status": <http status code>,
  "detail": "<what went wrong>",
  "instance": "<the request path>"
}

The type URL's final path segment is a stable, machine-readable code you can switch on — it never changes even if the title or detail wording does.

Validation error

400 — the request body failed schema validation: a required field is missing, a value is out of range, or a field has the wrong type. detail carries the first failing field's message.

{
  "type": "https://api.stector.app/errors/validation",
  "title": "Validation Error",
  "status": 400,
  "detail": "Name is required",
  "instance": "/api/v1/monitors"
}

Unauthorized

401 — the Authorization header is missing, the token doesn't match the expected format, or the token doesn't match any active PAT (including a revoked one). All four cases return this identical response — Stector never reveals which check failed.

{
  "type": "https://api.stector.app/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid or missing credentials",
  "instance": "/api/v1/teams/current"
}

Forbidden

403 — your token authenticated fine, but your team role doesn't have permission for this action. See each endpoint's Roles line on the Endpoints page for which roles can call it.

{
  "type": "https://api.stector.app/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "You do not have permission to perform this action.",
  "instance": "/api/v1/monitors/00000000-0000-0000-0000-000000000000"
}

Not found

404 — the resource doesn't exist, or it belongs to a different team than the one your token authenticates. Both cases return the same response, so a valid token can't be used to probe whether another team's resource exists.

{
  "type": "https://api.stector.app/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "The requested resource does not exist.",
  "instance": "/api/v1/monitors/00000000-0000-0000-0000-000000000000"
}

Rate limited

429 — you've exceeded the 300-per-minute limit, or hit the 60-second check-trigger lock on a specific monitor. Both cases use this same error code; the detail text tells you which one applies, and the check-trigger case adds a Retry-After header.

{
  "type": "https://api.stector.app/errors/rate-limited",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "Rate limit of 300 requests per 1 minute exceeded. Retry after 1 minute.",
  "instance": "/api/v1/monitors"
}

Internal error

500 — something went wrong on Stector's end. detail is always the same generic message — the real cause is logged server-side, never included in the response, so it can't leak internal details.

{
  "type": "https://api.stector.app/errors/internal",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred.",
  "instance": "/api/v1/teams/current"
}