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
| Scope | Limit | Applies |
|---|---|---|
| Per client IP | 30 requests/minute | Before authentication, to every endpoint including /health*. |
| Per tenant | 60 requests/minute sustained | After authentication. |
| Per tenant burst | 10 requests/second | After 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-Lengthis required on body-carrying methods (411 length_required); a malformed value is400 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
429and503, always honorRetry-After(header orretry_after_secondsin 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
4xxother than429, do not retry. The request is wrong; retrying reproduces the failure and burns rate budget. - Telemetry writes are not deduplicated. A
429or413was rejected before ingest and is safe to resubmit. But if aPOST /telemetrytimes 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.