# Error reference

<!-- GENERATED by gateway/scripts/generate-error-docs.mjs — DO NOT EDIT. This page renders gateway/contracts/errors.json; edit the catalogue and regenerate. -->

Every failure this API can return is listed below. Each error is an RFC 9457
Problem Detail sent as `application/problem+json`, and the `type` member of every
error body is a stable URI that resolves to its page in this reference — the link
in the body you are holding is the documentation for it. A `type` never changes
without a documented deprecation.

## The three 503s are different claims — do not treat them alike

Three errors share status `503` and they must not be read as one:

- [`ledger-unreachable`](/errors/ledger-unreachable) — the call was **never
  started and no Credits were consumed**. This is only ever sent when the failure
  was provably reached before any Spend could leave.
- [`spend-indeterminate`](/errors/spend-indeterminate) — **no claim is made about
  what was consumed**, because the outcome is not known. Any Credit recorded for
  the call is returned to the Account as a Refund.
- [`call-not-started`](/errors/call-not-started) — the call was **never
  started and no Credits were consumed** — the same claim `ledger-unreachable`
  makes, sent when the failure happened before the Ledger was called at all. There
  is no Spend to return, because none was ever recorded.

A monitor that counts every 503 as "no charge" will misread
the one that makes no claim about what was consumed; a monitor that
counts every 503 as "charged" will misread the two that state
plainly that nothing was. Branch on the `type`, not the status.

## The five 422s are five different things to do

Five errors share status `422`, and each asks for a different next action:

- [`ambiguous-address`](/errors/ambiguous-address) — the address matched more than
  one property. Choose one of the returned candidates, or add a city or ZIP.
- [`address-out-of-coverage`](/errors/address-out-of-coverage) — the state is not
  inside current data coverage yet. Coverage is temporary, not a product boundary.
- [`unscorable-address`](/errors/unscorable-address) — the property cannot be
  scored. Retrying the same address returns the same answer.
- [`idempotency-key-conflict`](/errors/idempotency-key-conflict) — the
  `Idempotency-Key` was already used for a different request. Nothing was charged;
  send the request again with a new key.
- [`property-not-found`](/errors/property-not-found) — no property record could be
  found for the identifier on the request. The Credit is Refunded. Check the
  identifier and send the request again.

All five are refusals about the request, never about our availability — and a
client branching on status alone cannot tell them apart. Branch on the `type`.

## The two 429s limit different things

Two errors share status `429`, and they are not the same limit:

- [`rate-limited`](/errors/rate-limited) — a limit on **how fast** requests arrive.
  It carries a `Retry-After` header, because the interval after which the request
  will be accepted is known.
- [`spend-cap-exceeded`](/errors/spend-cap-exceeded) — a ceiling on **how much** an
  Account may consume in a period. It carries **no** `Retry-After`, deliberately: a
  call costing more than the whole Spend Cap never fits, however long you wait, so no
  single honest interval exists. The body gives you the Spend Cap, the Credits already
  consumed, the Credits this call requires, and the length of the period in seconds —
  enough to compute your own backoff.

Slowing down fixes the first and does nothing for the second. A client branching on
status alone cannot tell them apart. Branch on the `type`.

## Every error

| Status | Type | Title | Retryable | Credits |
| --- | --- | --- | --- | --- |
| `401` | [`invalid-key`](/errors/invalid-key) | Invalid API Key | No | None consumed |
| `402` | [`insufficient-credits`](/errors/insufficient-credits) | Insufficient Credits | No | None consumed |
| `429` | [`rate-limited`](/errors/rate-limited) | Rate limit exceeded | Yes | None consumed |
| `400` | [`invalid-request`](/errors/invalid-request) | Malformed request | No | None consumed |
| `413` | [`payload-too-large`](/errors/payload-too-large) | Batch too large | No | None consumed |
| `503` | [`ledger-unreachable`](/errors/ledger-unreachable) | Ledger unreachable | Yes | None consumed |
| `503` | [`spend-indeterminate`](/errors/spend-indeterminate) | Spend outcome indeterminate | Yes | Returned as a Refund if recorded |
| `504` | [`upstream-timeout`](/errors/upstream-timeout) | Upstream service timed out | Yes | Refunded |
| `502` | [`upstream-unavailable`](/errors/upstream-unavailable) | Upstream service unavailable | Yes | Refunded |
| `422` | [`ambiguous-address`](/errors/ambiguous-address) | Address is ambiguous | No | Refunded |
| `422` | [`address-out-of-coverage`](/errors/address-out-of-coverage) | Address outside current data coverage | No | Refunded |
| `422` | [`unscorable-address`](/errors/unscorable-address) | Address cannot be scored | No | Refunded |
| `422` | [`idempotency-key-conflict`](/errors/idempotency-key-conflict) | Idempotency-Key conflict | No | None consumed |
| `409` | [`duplicate-request-in-flight`](/errors/duplicate-request-in-flight) | Duplicate request in flight | Yes | None consumed |
| `404` | [`unknown-endpoint`](/errors/unknown-endpoint) | No such endpoint | No | None consumed |
| `503` | [`call-not-started`](/errors/call-not-started) | Call never started | Yes | None consumed |
| `403` | [`terms-not-accepted`](/errors/terms-not-accepted) | Terms of Service not accepted | No | None consumed |
| `429` | [`spend-cap-exceeded`](/errors/spend-cap-exceeded) | Spend Cap reached | Yes | None consumed |
| `422` | [`property-not-found`](/errors/property-not-found) | Property not found | No | Refunded |

The Credits column is a summary; each page states the full outcome. "Refunded"
means the response itself says whether the Refund is applied or pending, and a
Refund is visible in the Portal's Ledger history like any other entry.

Contact support at `john@investorliftdata.com` and quote the Trace ID.
