Skip to content

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.

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.
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.
{
"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
}

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.

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.