Errors
Errors are RFC 9457 problem details with media type application/problem+json. Errors are never billed.
Branch on type, never on title or detail. Every error carries request_id, also sent as the X-Request-Id header. Quote it to support.
Every error type
Section titled “Every error type”type |
Status | Title | Meaning | Retry | What to do |
|---|---|---|---|---|---|
invalid_request |
400 | Invalid request | The request is malformed, has unknown fields or breaks a size limit. | no | Fix the request. errors[] holds a JSON Pointer and a message per bad field. Do not retry unchanged. |
unauthorized |
401 | Unauthorized | The API key is missing, malformed, revoked or expired. | no | Check the Authorization: Bearer vd_live_... header. The key may be revoked or expired; create or rotate one. |
forbidden_scope |
403 | Forbidden | The key lacks the scope this endpoint needs, or the resource belongs to another org. | no | Use a key with the scope the endpoint lists, or add the scope with PATCH /v1/keys/{id}. |
not_found |
404 | Not found | The resource does not exist in this organization. | no | The id does not exist in your organization. Check the id; firms resolve with GET /v1/firms/resolve. |
insufficient_balance |
402 | Insufficient balance | The balance cannot cover the call; top up. | after top-up | Top up with POST /v1/topup (returns a Checkout URL), then retry with the same Idempotency-Key. |
max_cost_exceeded |
402 | Maximum cost exceeded | The call would cost more than X-Max-Cost-USD; nothing was spent. |
no | The call costs more than your X-Max-Cost-USD. Raise the cap or skip the call. Nothing was spent. |
rate_limited |
429 | Rate limited | Too many requests; wait Retry-After seconds. |
yes | Wait Retry-After seconds, then retry with the same Idempotency-Key. Watch X-RateLimit-Remaining. |
idempotency_conflict |
409 | Idempotency conflict | The Idempotency-Key was reused with a different request, or the first request is still running. |
maybe | The key was reused with a different body: send a new UUID. If the first request is still running, retry shortly. |
vendor_unavailable |
503 | Vendor unavailable | A vendor the call depends on is unavailable; nothing was billed. | yes | Phase 2 only. Wait Retry-After seconds and retry with the same Idempotency-Key. |
decision_unavailable |
503 | Decision unavailable | The decision model is temporarily unavailable or decisions are switched off; retry after Retry-After. Nothing was billed. |
yes | The decision model is down or decisions are switched off. Wait Retry-After seconds and retry with the same Idempotency-Key. Nothing was billed. |
internal |
500 | Internal error | An unexpected server error; nothing was billed. Retry with the same Idempotency-Key. |
yes | Retry with the same Idempotency-Key and backoff. If it persists, send the request_id to support. |
Extra fields
Section titled “Extra fields”| Field | Present on |
|---|---|
errors |
invalid_request: one entry per bad field, with a JSON Pointer path and a message. |
required_usd |
402 errors: what the call would cost. |
balance_usd |
insufficient_balance: your current balance. |
retry_after_seconds |
429 and 503: seconds to wait, equal to Retry-After. |
Example
Section titled “Example”{ "type": "max_cost_exceeded", "title": "Maximum cost exceeded", "status": 402, "detail": "This call costs $0.005, above X-Max-Cost-USD 0.001. Nothing was spent.", "request_id": "req_01JC3Z4Y8R9K2M5N7P1Q3S5T7V", "required_usd": 0.005}Retrying safely
Section titled “Retrying safely”Retry only the types marked “yes” above, and always with the same Idempotency-Key. A retried POST that already succeeded returns the stored response and bills nothing. Honour Retry-After. Add jitter. Stop after a few attempts.
Rate limits
Section titled “Rate limits”Each key allows 600 requests per minute by default, with a burst of 100. Your org can raise it. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Over the limit you get 429 rate_limited with Retry-After.