# Errors and retries

Most request-level errors use `error`, `detail`, `retryable`, and `requestId`. `POST /api/v1/identify` and `POST /api/v1/enrich` can instead return a batch envelope (`version`, `requestId`, `count`, `results`, and `quota`) for all-invalid or all-upstream-failed batches. Product reads may return a compact `{ "error", "requestId" }` body. Keep the request ID for support, and do not expose API keys or customer evidence in a support report.

| HTTP status | Meaning | Action |
| --- | --- | --- |
| `400` | Invalid input or unsupported selector combination | Correct the request; do not retry unchanged. |
| `401` | Missing or invalid API key | Follow `setupUrl` and `steps`, or replace the key in the dashboard. |
| `404` | Resource or result reference is absent, expired, or inaccessible | Confirm the identifier and account ownership; do not infer a market value. |
| `409` | Idempotency conflict, request still running, mapping-selector conflict, or stale mapping revision | Reuse a key only for the identical request body. Resolve conflicts using the response detail. |
| `413` / `415` | Body too large or not JSON | Reduce the request or set `Content-Type: application/json`. |
| `429` | Account quota, service safety ceiling, or short-term rate limit | Honor `Retry-After`; use `retryAt` when supplied. Do not split batches or create accounts to evade limits. |
| `502` / `503` | All required upstream work failed or a required service is unavailable | Preserve any earlier successful identity, retry only when `retryable` and within a bounded backoff. A provider failure is not a no-match. |

Use one stable `Idempotency-Key` for each logical paid operation and reuse it only with the identical body on a network retry. A changed body needs a new key. Identical single-product enrich requests without an explicit key replay for 30 seconds. Explicit idempotency records are account-scoped; a pending reservation older than 10 minutes is expired and refunded. A `request_expired` response is terminal for that key: use a new key only when intentionally starting a new logical operation.

For 429 responses, the beta account allowance counts attempts until monthly plans activate. `account_quota_exceeded` returns `retryAt` and `Retry-After`. `daily_limit` is the separate service-wide ceiling; `rate_limited` is a short-term request limit. Read [monthly plans](./monthly-plans.md) for the active policy and account upgrade path. An outage or empty price result must never be represented as `$0`.
