Data Reference

Errors

Error envelope shape, error codes, retry guidance, and rate-limit headers.

Every API error uses one error envelope with a machine code, a human message, and the request id.

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Vault not found",
    "request_id": "3f6c1a2e-9b7d-4c1e-8f2a-1d2e3f4a5b6c"
  }
}

Codes are uppercase. During the one-release compatibility window for the PHI-295 migration, a response whose code changed from a legacy lowercase value also carries that previous value as error.legacy_code.

{
  "error": {
    "code": "UNAUTHORIZED",
    "legacy_code": "invalid_api_key",
    "message": "API key is invalid or has been revoked",
    "request_id": "3f6c1a2e-9b7d-4c1e-8f2a-1d2e3f4a5b6c"
  }
}

Request Ids

Every response carries an X-Request-Id header, and every error body carries the same value as request_id. Quote it when reporting a failure: support can find the request, its key, endpoint, status, and latency from that id alone.

Send your own X-Request-Id (1 to 64 letters, digits, underscores, or dashes) and it is echoed back unchanged, so you can correlate with your own logs; any other value is replaced with a generated id. Error responses are sent with Cache-Control: no-store, so a request id is never replayed from a cache.

Error Codes

CodeHTTP statusMeaningRetry guidance
BAD_REQUEST400Invalid path, query, or body inputFix the request before retrying
UNAUTHORIZED401Authentication is missing or invalidAdd a valid bearer key or session
FORBIDDEN403The supplied key or session is not allowedDo not retry until access changes
NOT_FOUND404Resource not found or feature disabledCheck the identifier or feature availability
CONFLICT409Request conflicts with existing stateRefresh state before retrying
GONE410A resource is no longer availableFollow a successor link when one is supplied
VERSION_RETIRED410Basket version was retiredFollow the successor URL
PAYLOAD_TOO_LARGE413Request body exceeds the API body limitSend a smaller body
UNSUPPORTED_MEDIA_TYPE415Request content type is unsupportedSend a supported Content-Type
RATE_LIMITED429Rate limit exceededRetry after the Retry-After value
SUBCLIENT_RATE_LIMITED429A shared-key integration's per-client budget was exceededRetry after the Retry-After value
ONDO_API_UNAVAILABLE502The upstream Ondo API could not serve an admin previewRetry with backoff
SERVICE_UNAVAILABLE503Fail-safe, graph materialization, Redis, or upstream data is unavailableRetry after the Retry-After value when present
INTERNAL_ERROR500Unexpected server errorRetry with backoff and include request_id if reporting
GATEWAY_TIMEOUT504A read exceeded its time budgetRetry after the Retry-After value

The route-specific codes in the table stay uppercase and use the same envelope.

For one compatibility release, these former lowercase values appear only as error.legacy_code:

Legacy codeCanonical code
invalid_api_keyUNAUTHORIZED
tier_requiredUNAUTHORIZED without a key; otherwise FORBIDDEN
too_many_connectionsRATE_LIMITED
bad_requestBAD_REQUEST
validation_errorBAD_REQUEST
not_foundNOT_FOUND
royco_market_not_indexedNOT_FOUND
internal_errorINTERNAL_ERROR
forbiddenFORBIDDEN
feature_not_enabledFORBIDDEN
upgrade_requiredFORBIDDEN
key_limit_reachedCONFLICT
overloadedSERVICE_UNAVAILABLE
unavailableSERVICE_UNAVAILABLE
signing_unavailableSERVICE_UNAVAILABLE

VERSION_RETIRED is emitted by immutable basket version routes. This retired-version response is a legacy exception that includes the field success set to false alongside the error object and includes error.request_id. Like other errors, it is sent with Cache-Control: no-store. Its X-Request-Id header still identifies the current request. Parse error.code and use the successor pointer plus the Link header with rel="successor-version".

{
  "success": false,
  "error": {
    "code": "VERSION_RETIRED",
    "message": "Basket version 42 for \"rwa-tbill-conservative\" was retired. Use the latest version at /v1/baskets/rwa-tbill-conservative.",
    "retired_at": "2026-07-01T00:00:00Z",
    "successor_url": "https://api.philidor.io/v1/baskets/rwa-tbill-conservative",
    "request_id": "3f6c1a2e-9b7d-4c1e-8f2a-1d2e3f4a5b6c"
  }
}

Rate Limit Headers

Responses include rate-limit headers when rate limiting is applied.

HeaderMeaning
X-RateLimit-LimitRequests allowed in the active window
X-RateLimit-RemainingRequests remaining in the active window
X-RateLimit-ResetUnix timestamp for reset
X-RateLimit-WindowWindow length such as 60s
Retry-AfterSeconds to wait before retrying
X-Request-IdCorrelation id, also in every error body

Anonymous public reads use IP-based limits. API-key requests use the key's configured per-minute limit on public routes.

Retry Pattern

Handle 429 and 503 first. If Retry-After is present, wait that many seconds before retrying.

Use exponential backoff for 500 responses. Do not retry 400, 401, 403, or 404 without changing the request or credentials.

On this page

Raw