# Handle errors

import { Card } from "zudoku/ui/Card";
import { CircleAlert, RotateCcw } from "zudoku/icons";

Branch on the error's `type` to choose the right fix before you retry.

## Read the error

Every error has a stable `type` address that opens its page in the [error catalogue](/errors).
Use that address in your error handling; HTTP status alone cannot tell you what to do.

Read `detail` for the explanation and the error page for what happened to the charge on your [balance](/definitions), the money available for API calls.

Log the [Trace ID](/definitions), the identifier for this call, from the `trace-id` response header.
Errors also carry it as `trace_id` in the body.
Quote it to `support@investorliftdata.com` when asking about a call.

## Separate the 503 errors

[`ledger-unreachable`](/errors/ledger-unreachable), [`call-not-started`](/errors/call-not-started) and [`route-unavailable`](/errors/route-unavailable) mean the call never started and nothing was charged.
For `route-unavailable`, other endpoints still work.

[`spend-indeterminate`](/errors/spend-indeterminate) means the outcome is unknown: no claim is made about what was consumed.
Any charge recorded for the call is returned to your balance.
For all four, wait for `Retry-After` before sending again.

## Fix the right 422 condition

- [`ambiguous-address`](/errors/ambiguous-address): choose a returned candidate, or add the unit number, city or ZIP.
- [`property-not-found`](/errors/property-not-found): check the identifier, or look up the property's current id.
- [`address-out-of-coverage`](/errors/address-out-of-coverage): the state is outside current coverage; changing the request cannot extend coverage.
- [`unscorable-address`](/errors/unscorable-address): retrying the same address gives the same answer; try another address.
- [`idempotency-key-conflict`](/errors/idempotency-key-conflict): use a new key for the changed request; nothing was charged for the conflict.

The first four return the charge to your balance; `detail` states whether the return is applied or pending.
They do not all have the same retry remedy.

## Separate the three 429 limits

[`rate-limited`](/errors/rate-limited) limits how fast requests arrive.
Wait for `Retry-After` and reduce your sending rate.

[`spend-cap-exceeded`](/errors/spend-cap-exceeded) limits how much your [account](/definitions), the record that owns your keys and the money you add, may spend in a rolling window.
It has no `Retry-After`; slowing down alone does not fix it.

Wait for earlier calls to leave the window, or ask support to raise the cap.
A call costing more than the entire cap cannot fit however long you wait.

[`free-call-limit`](/errors/free-call-limit) limits free calls: 500 a day per account, across the app, the API and assistants.
Free calls start again at 00:00 UTC, the time in `resets_at`; `Retry-After` gives the wait in seconds.
Paid calls are not affected.

None of the three 429s charges anything.

## Control retries

The API never retries for you.
Use an `Idempotency-Key` for [safe retries](/production/retry-safely).
For a `502` or `504`, read the refund state, then retry as a fresh attempt.

## Next steps

<div className="docs-next-steps">

<Card className="docs-next-card"><a href="/errors"><CircleAlert className="docs-next-icon" size={18} strokeWidth={1.5} aria-hidden="true" focusable="false" /><strong>Error catalogue</strong><span className="docs-next-description">Find the exact cause and fix.</span><span className="docs-next-arrow" aria-hidden="true">→</span></a></Card>

<Card className="docs-next-card"><a href="/production/retry-safely"><RotateCcw className="docs-next-icon" size={18} strokeWidth={1.5} aria-hidden="true" focusable="false" /><strong>Retry safely</strong><span className="docs-next-description">Reuse a key for one logical call.</span><span className="docs-next-arrow" aria-hidden="true">→</span></a></Card>

</div>
