> ## Documentation Index
> Fetch the complete documentation index at: https://jesse-7a8b4a1d.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every status the API answers, what the body says, and which ones are worth retrying.

Refusals are HTTP statuses on the call that was refused. Nothing is started and nothing is billed unless the `POST` answered `202`.

| Status | When | Body |
| - | - | - |
| `400` | `q` is present but only whitespace. | `{"detail": "Empty query"}` |
| `401` | No credential, or one that does not resolve. | `{"detail": "Missing credentials"}` when the `Authorization` header is absent or carries nothing after the scheme; `{"detail": "Invalid credentials"}` when the credential does not resolve to an account. |
| `402` | Your available balance cannot cover the reservation. | See below. Nothing was started. |
| `403` | The account behind the credential is closed. | `{"detail": "Account disabled"}` |
| `404` | The run is not yours, does not exist, or the id is malformed. | `{"detail": "Unknown search."}` All three answer identically on purpose: a run id is not a way to discover another account's searches. |
| `422` | A field failed validation: `q` shorter than 3 or longer than 2000 characters, `target` outside 1–50, a negative `since`. | The standard validation body, a `detail` array naming the field that failed. Nothing was reserved. |
| `500` | The credential could not be looked up, or the reservation could not be opened. | `{"detail": "Internal server error"}` or `{"detail": "The reservation for this search could not be opened."}`. Both are safe to retry once. |

Check what you sent before retrying a `401`: an API key starts `lf_live_` and a session token starts `eyJ`. A `403` and a `422` will answer the same way for ever, so fix the request rather than repeating it.

## `402 Payment Required`

This is the ordinary out-of-funds signal, not a fault. A search holds $5.00 of your balance before it does any work, so a caller with $2.00 is refused up front rather than finding out after the providers have been paid.

```json theme={null}
{
  "detail": {
    "error": "insufficient_balance",
    "message": "This search needs $5.00 available. You have $2.00.",
    "availableMicrousd": 2000000,
    "availableUsd": "2.000000",
    "heldMicrousd": 1000000,
    "neededMicrousd": 5000000,
    "neededUsd": "5.000000"
  }
}
```

`availableMicrousd` is what you may spend, which is your balance minus `heldMicrousd`, the part already committed to runs in flight. `neededMicrousd` is the reservation, not the price of the search. The three `*Microusd` fields are integers in micro-USD; the two `*Usd` fields are the same figures as strings.

<Warning>
  **Do not retry a `402` on a timer.** Nothing about the request was wrong, so a retry fails identically until the balance changes. It changes in two ways: a top-up, or a run in flight finishing and releasing its hold. Branch on `error == "insufficient_balance"`, surface the `message`, and either wait for a run to land or send the user to top up. A retry loop here burns quota and tells the user nothing.
</Warning>

## When the search itself fails

A run that was accepted and then failed is not an HTTP error anywhere: the `POST` already returned `202`. It shows up as an `error` event in the stream, followed by `cost`, `usage` and `done`, and the run lands with `status: "error"`.

| Situation | What you see |
| - | - |
| The search could not finish | `error` with a short message, then `cost`, `usage` and `done`. `status` is `error`. |
| You called stop | `final` with `stopped: true`, then `cost` at stage `Stopped`, `usage` and `done` with `stopped: true`. `status` is `stopped`. |
| The balance ran out mid-run, or the run hit its ceiling | The same partial tail as a stop. `status` is `budget_exhausted`. You are charged for what it spent. |
| The run outlived its deadline, or its process went away | `status` is `interrupted`, with `stopReason: "deadline"` in the first case. There may be no tail at all, so `result` can be `null`. `where` says how far it got. |

The `402` case leaves one more trace worth knowing about: a run row with `status: "error"` and no events, because the run document is written before the reservation is attempted. It will appear in `GET /v1/searches`.

## Key management errors

| Status | When | Body |
| - | - | - |
| `401` | `POST`, `GET` or `DELETE /api/keys` called with an API key instead of a session token, or with no token. | An API key gets `{"detail": "Invalid token"}`, because it is not a session token and these routes take nothing else. A missing or empty header gets `{"detail": "Missing bearer token"}`, and a valid token for an account that no longer exists gets `{"detail": "Unknown user"}`. The refusal is deliberate; see [Access](/access). |
| `403` | The account is closed. | `{"detail": "Account is deleted"}` |
| `404` | The key id is not yours, does not exist, or is already revoked. | `{"detail": "Key not found"}` |
| `422` | `name` is missing, empty, or longer than 100 characters. | The standard validation body. |
| `400` | `name` is present but only whitespace. | `{"detail": "name is required"}` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.