Errors
When the gateway refuses a request (the proxy, the OpenTelemetry receiver at /v1/traces, or batch event ingest), the body has one shape:
{
"error": "Invalid API key",
"code": "UNAUTHORIZED",
"hint": "No active key matches this value. ...",
"docs": "https://docs.metrxbot.com/quickstart"
}erroris the human-readable message (kept for clients that already match on it).codeis the stable value to branch on.hintnames the fix.docslinks the page that explains it.
Some codes carry extra fields alongside, listed below.
Authentication
MISSING_API_KEY
401. The Authorization header is missing or does not start with Bearer .
Send your Metrx API key as Authorization: Bearer <key>. For an OpenTelemetry exporter, set OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<key>", with the space percent-encoded.
INVALID_API_KEY_FORMAT
401. The value after Bearer is too short to be a Metrx API key.
Copy the full key (it starts with sk_live_) from Settings → Security → API Keys.
UNAUTHORIZED
401. No active key matches the value sent. Check the key was copied in full, has not been revoked, and belongs to this environment; or create a new one in Settings → Security → API Keys.
ORG_INACTIVE
403. The organization that owns the key is deactivated. An org owner can reactivate it, or contact support.
INSUFFICIENT_SCOPE
403. The key is valid but lacks the scope this request needs. GET, HEAD and OPTIONS need read; every other method needs write, including proxied LLM calls, because they are recorded. The body carries required_scope and granted_scopes.
Use a key with that scope. An org owner or admin can create one in Settings → Security → API Keys.
SCOPE_CHECK_UNAVAILABLE
503. The gateway could not read this key’s scopes, so it refused the request rather than guess.
Retry after the Retry-After interval. If it persists, contact support.
Limits
PAYMENT_REQUIRED
402. The plan has used its monthly call limit.
Upgrade at the upgrade_url in the body, or wait for metrx_usage_summary.reset_at. The body also carries current_tier, limit_hit and upgrade_options.
RATE_LIMITED
429. More requests this minute than the plan allows.
Wait for the number of seconds in the Retry-After header (also retry_after in the body), or batch events.
BUDGET_EXCEEDED
429. A budget with hard_block enforcement has reached its limit.
Raise the limit with Edit limit at Settings → Budgets (then Resume the budget there if it is paused), or wait for the period to reset. The body carries budget_id, spent, limit and enforcement. If you cannot change budgets, ask your admin.
Requests
NOT_FOUND
404. No gateway route matches the method and path.
The body’s available_routes lists the routes. A common cause is an OpenAI client whose base URL lacks /v1: use https://gateway.metrxbot.com/v1.
PAYLOAD_TOO_LARGE
413. The body is over the size limit: 10MB for proxied requests, 1MB for OpenTelemetry.
For OpenTelemetry, lower the exporter batch size: OTEL_BSP_MAX_EXPORT_BATCH_SIZE=100.
INVALID_JSON
400. The body is not valid JSON. The hint depends on the endpoint:
- Proxy: the body must be a JSON provider request, e.g. a chat/completions payload.
- OpenTelemetry: send an OTLP
ExportTraceServiceRequestas JSON (OTEL_EXPORTER_OTLP_PROTOCOL=http/json). - Batch ingest: send a JSON body of the form
{"events": [...]}.
UNSUPPORTED_MODEL
400. The gateway could not map the model to a provider.
Send an X-Provider header, or GET /v1/models for the supported list. The body carries model and supported_providers.
MISSING_PROVIDER_KEY
400. A proxied request has no X-Provider-Key header.
Pass your LLM provider’s API key in X-Provider-Key. Your Metrx key stays in Authorization.
OpenTelemetry
OTLP_PROTOBUF_UNSUPPORTED
415. The receiver accepts OTLP/HTTP JSON only, and the request was protobuf (or another content type). gRPC is not supported.
Set OTEL_EXPORTER_OTLP_PROTOCOL=http/json (or OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/json). Exporters that cannot send JSON, such as Python’s, go through a Collector: see OpenTelemetry.
Batch event ingest
INVALID_EVENTS
400. The body has no events array.
Send an events array of LLM events; model and latency_ms are required on each.
BATCH_TOO_LARGE
400. More than 100 events in one request.
Split the batch into requests of at most 100 events. The body carries received, the number sent.
Batch event warnings
A 200 from batch ingest can carry warnings, one { index, code } per event that was accepted with a field dropped. The event is stored; only that field is lost. The TypeScript exporter logs each warning.
task_id_not_uuid
The event’s task_id is a string but not a UUID, so it was stored without one and its outcome cannot join to it.
Send a UUID. The SDKs’ task() helpers map any string id to a UUIDv5 for you; to do it yourself, use namespace 522984cb-f1f3-5643-933b-8ba17bed1467.
task_id_not_string
The event’s task_id is not a string (for example a number or an object), so it was stored without one.
Send the task id as a UUID string.