Skip to main content

Errors and limits

Errors use a flat JSON envelope: an error code string plus code-specific extras. Retryable responses (429, 503) also carry a Retry-After header.

{ "error": "rate_limited", "retry_after_seconds": 21 }

The one exception is request-body schema validation, which returns the framework-standard 422 shape described below.

Errors by status

400 Bad Request

Invalid parameter values or a malformed Content-Length header.

{ "error": "invalid_input", "field": "entity_type" }
{ "error": "invalid_content_length" }
{ "error": "tenant_required" }

401 Unauthorized

Missing or invalid x-api-key. Do not retry; fix the key. See Authentication.

{ "error": "auth_failed" }

403 Forbidden

The key is valid but the tenant has been disabled.

{ "error": "tenant_disabled" }

404 Not Found

The key maps to a tenant this environment does not recognize.

{ "error": "unknown_tenant" }

411 Length Required

Body-carrying requests must send Content-Length. Chunked uploads without it are rejected.

{ "error": "length_required" }

413 Payload Too Large

Two distinct causes, two distinct codes:

{ "error": "request_too_large", "max_bytes": 1048576, "received_bytes": 2097152 }
{ "error": "batch_too_large", "max": 1000, "received": 1500 }

The first is the global 1 MB body cap; the second is the 1,000-record telemetry batch cap. Both mean: split the payload, do not retry as-is.

422 Unprocessable Entity

The body parsed as JSON but failed schema validation (for example, a telemetry record with an unparseable time). The shape is a detail list locating each failure:

{
"detail": [
{ "loc": ["body", 4, "time"], "msg": "invalid datetime format", "type": "value_error" }
]
}

429 Too Many Requests

Two distinct causes:

{ "error": "auth_lockout", "retry_after_seconds": 842 }
{ "error": "rate_limited", "retry_after_seconds": 21 }

auth_lockout means this IP failed authentication 5 times in 300 seconds and is locked for 900 seconds. rate_limited means a rate limit below was exceeded. Both include Retry-After.

503 Service Unavailable

The platform is up but a dependency is not. All carry Retry-After where meaningful.

{ "error": "telemetry_unavailable", "upstream": "influx", "retry_after_seconds": 5 }
{ "error": "data_plane_unavailable" }
{ "error": "tenant_unavailable" }

telemetry_unavailable is a transient time-series backend outage. data_plane_unavailable and tenant_unavailable mean the tenant's dedicated data plane cannot be routed to; if it persists, contact the platform team rather than retrying harder.

Rate limits

ScopeLimitApplies
Per client IP30 requests/minuteBefore authentication, to every endpoint including /health*.
Per tenant60 requests/minute sustainedAfter authentication.
Per tenant burst10 requests/secondAfter authentication.

The middleware order matters: body-size checks and the per-IP limit run before authentication, the per-tenant limit runs after. Unauthenticated probes and failed-auth retries consume your per-IP budget, so a broken client can starve a healthy one on the same gateway IP.

Body limits

  • Maximum request body: 1 MB (413 request_too_large).
  • Maximum telemetry batch: 1,000 records (413 batch_too_large).
  • Content-Length is required on body-carrying methods (411 length_required); a malformed value is 400 invalid_content_length.

In practice the 1,000-record cap binds before the 1 MB cap for typical telemetry records. Batch loops should chunk on record count first; see Ingest telemetry.

Retry guidance

  • On 429 and 503, always honor Retry-After (header or retry_after_seconds in the body). Add jitter if many clients share a schedule.
  • On network errors and timeouts for reads (GET /topology, GET /predictions, /health*), retry with exponential backoff; reads are safe to repeat.
  • On 4xx other than 429, do not retry. The request is wrong; retrying reproduces the failure and burns rate budget.
  • Telemetry writes are not deduplicated. A 429 or 413 was rejected before ingest and is safe to resubmit. But if a POST /telemetry times out or the connection drops after the request was sent, the batch may have been written; blindly resubmitting risks double-writing every record. Prefer logging ambiguous batches for reconciliation over automatic replay, and see the Telemetry idempotency warning.