Skip to main content
Errors from the Data API carry a stable code beside the legacy detail member. Branch on code in your own handling, and fall back to the HTTP status when code is absent. The set of codes is open, so treat an unknown value as its HTTP status class. The type member of every error body is a URL of the form https://www.vaquill.ai/docs/errors/{code}. Opening that URL in a browser lands on this page, at the section for that code. For the legacy status table and the detail and errors shapes, see Errors.

Body fields

Example response

Every error body has this shape. The detail text is illustrative and can differ by endpoint. retryAfterSeconds appears only on a 429 or 503 that carries a Retry-After header. A few codes add members of their own, which their sections name.

All codes

Codes by kind

Each section below is named for its code, so the type URL of an error body opens straight to it. Every code is exposed in both SDKs with code, retryable, the request id and the retry hint, as the SDK section further down describes.

Authentication and permission

authentication_required

Status 401. Retryable: No. Python vaquill.AuthenticationError. TypeScript AuthenticationError. What it means. The request carried no credential. What to do. Send your key in the Authorization: Bearer vq_key_... header. See Authentication for the header format and key rotation. Do not retry the same request without a key.

invalid_api_key

Status 401. Retryable: No. Python vaquill.AuthenticationError. TypeScript AuthenticationError. What it means. The key is malformed, unknown or revoked. This is also the default code for any 401 that the API could not classify more precisely. What to do. Check that the key starts with vq_key_ and was copied whole. Create a new key in the developer dashboard if the old one was revoked. Do not retry with the same key.

api_key_expired

Status 401. Retryable: No. Python vaquill.AuthenticationError. TypeScript AuthenticationError. What it means. The key is past its expiry date. What to do. Create a new key and replace the expired one. Do not retry with the same key.

permission_denied

Status 403. Retryable: No. Python vaquill.PermissionDeniedError. TypeScript PermissionDeniedError. What it means. The key is valid, but your plan, the key’s scope or the requested jurisdiction does not allow this call. For example, webhook delivery on a watch requires the Business plan. What to do. Read detail to see which of plan, scope or jurisdiction applies. Use a different channel or jurisdiction, or upgrade the plan. Retrying the same request cannot succeed.

Credits and limits

insufficient_credits

Status 402. Retryable: No. Python vaquill.InsufficientCreditsError. TypeScript InsufficientCreditsError. What it means. Your credit balance is too low to pay for this call. What to do. Top up your credits, then send the request again. GET /credits/balance is free and shows what you hold. Never retry before topping up. The SDKs do not retry this code.

rate_limited

Status 429. Retryable: Yes. Python vaquill.RateLimitError. TypeScript RateLimitError. What it means. You exceeded the per-key rate window. What to do. Wait for the number of seconds in the Retry-After header, which the API also returns as retryAfterSeconds, then retry. Use exponential backoff if no hint is present. The SDKs retry this code automatically.

daily_cap_exceeded

Status 429. Retryable: No. Python vaquill.DailyCapExceededError. TypeScript DailyCapExceededError. What it means. The key reached its per-key daily cap. The status is 429, but unlike rate_limited a short wait does not help. What to do. Retry only after the daily cap resets. The SDKs do not retry this code.

Request validation

validation_error

Status 422. Retryable: No. Python vaquill.UnprocessableEntityError. TypeScript UnprocessableEntityError. What it means. The request body or parameters failed schema validation. This is the default code for a 4xx that the API could not classify more precisely. The body also carries the legacy errors array, a field-level list. What to do. Read the errors array: each entry has loc (where), msg (why) and type. Fix the listed fields and send the request again. See Errors for the legacy errors shape.

invalid_query

Status 422. Retryable: No. Python vaquill.UnprocessableEntityError. TypeScript UnprocessableEntityError. What it means. A Boolean query or a filter was refused before any charge. What to do. Read detail for the reason, such as a malformed expression. See Boolean Search for the grammar and its error messages. Rejected requests are not charged.

invalid_citation_input

Status 422. Retryable: No. Python vaquill.UnprocessableEntityError. TypeScript UnprocessableEntityError. What it means. The request itself is malformed. This is not the same as a citation that parsed but did not resolve. What to do. Fix the request as detail describes. Send the citation as a string in the field the endpoint documents.

Not found

section_not_found

Status 404. Retryable: No. Python vaquill.NotFoundError. TypeScript NotFoundError. What it means. No section matches the identifier you sent. The body keeps the legacy members reason and didYouMean beside the standard ones, and they carry the values the lookup produced. What to do. Read reason to see why the lookup failed. Try the candidates in didYouMean when present. Use search or the hierarchy browse to find the correct identifier.

resource_not_found

Status 404. Retryable: No. Python vaquill.NotFoundError. TypeScript NotFoundError. What it means. A watch, board, delivery or change with that id does not exist for your account. What to do. Check the id and that it belongs to your key’s account. List the resource first to get valid ids.

Idempotency and conflict

idempotency_key_reused

Status 422. Retryable: No. Python vaquill.IdempotencyKeyReusedError. TypeScript IdempotencyKeyReusedError. What it means. You sent an Idempotency-Key that was already used with a different request. What to do. Use a fresh key for each distinct request. Reuse a key only when you resend the identical request.

idempotency_in_progress

Status 409. Retryable: Yes. Python vaquill.IdempotencyInProgressError. TypeScript IdempotencyInProgressError. What it means. The original request with this Idempotency-Key is still running. What to do. Wait briefly and resend the same request with the same key. The SDKs retry this code automatically.

idempotency_replay_unavailable

Status 409. Retryable: No. Python vaquill.IdempotencyReplayUnavailableError. TypeScript IdempotencyReplayUnavailableError. What it means. The original request completed, but its response was too large to replay. The call was not charged again. What to do. Check your balance with GET /credits/balance before deciding to repeat the call. If you need the data again, send a new request with a new key.

conflict

Status 409. Retryable: No. Python vaquill.ConflictError. TypeScript ConflictError. What it means. The request conflicts with the current state of a resource and no more specific code applies. For example, you already hold the watch you tried to create. What to do. Read detail for the conflicting state. Fetch the existing resource instead of creating it again.

Server and upstream

upstream_timeout

Status 503. Retryable: Yes. Python vaquill.InternalServerError. TypeScript InternalServerError. What it means. A service the API depends on did not answer in time. The call is refunded. What to do. Retry with exponential backoff. Honour Retry-After when it is present. The SDKs retry this code automatically.

service_unavailable

Status 503. Retryable: Yes. Python vaquill.InternalServerError. TypeScript InternalServerError. What it means. A dependency of the API is down. The call is refunded. What to do. Retry with exponential backoff. Honour Retry-After when it is present. The SDKs retry this code automatically.

internal_error

Status 500. Retryable: Yes. Python vaquill.InternalServerError. TypeScript InternalServerError. What it means. The API failed in a way it did not anticipate. The call is refunded. What to do. Retry with exponential backoff. If it persists, contact support and include the requestId. The SDKs retry this code automatically.

How the SDKs expose them

Both SDKs read the same body and map it to an exception class. They pick the class from code first and from the HTTP status second, so an unknown code still raises the right status class. code and retryable are None in Python and null in TypeScript when the server did not send them.

Class by code

Every other code maps by status: 401 to AuthenticationError, 403 to PermissionDeniedError, 404 to NotFoundError, 409 to ConflictError, 422 to UnprocessableEntityError, 429 to RateLimitError, and 5xx to InternalServerError. The three idempotency classes share the base class IdempotencyConflictError.

Automatic retries

The SDKs retry rate_limited, idempotency_in_progress, upstream_timeout, service_unavailable and internal_error. They never retry insufficient_credits or daily_cap_exceeded. See the Python SDK and the TypeScript SDK pages for client setup.
Last modified on November 6, 2026