> ## Documentation Index
> Fetch the complete documentation index at: https://docs.walletlink.social/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Error shape and every code the API returns.

Errors return a JSON body with a human-readable `error` and a stable machine-readable `code`.

```json theme={null}
{
  "error": "Invalid wallet address format. Expected 0x followed by 40 hex characters.",
  "code": "INVALID_ADDRESS"
}
```

Branch on `code`. The `error` string is written for humans and may be reworded.

## Codes

| Code                         | Status | Cause                                                                                                                                                                                                                                       |
| ---------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MISSING_API_KEY`            | 401    | No `Authorization` header.                                                                                                                                                                                                                  |
| `INVALID_API_KEY`            | 401    | Key is unrecognized, revoked, or expired.                                                                                                                                                                                                   |
| `RATE_LIMIT_EXCEEDED`        | 429    | A per-minute, per-day or per-month limit was hit. See [rate limits](/api-reference/rate-limits).                                                                                                                                            |
| `INVALID_ADDRESS`            | 400    | An address is not `0x` plus 40 hex characters.                                                                                                                                                                                              |
| `INVALID_HANDLE`             | 400    | X handle is not 1 to 15 characters of letters, numbers or underscores.                                                                                                                                                                      |
| `INVALID_USERNAME`           | 400    | Farcaster username is not 1 to 32 characters of letters, numbers, dots or hyphens, starting with a letter or a number.                                                                                                                      |
| `INVALID_PARAMETER`          | 400    | A query parameter is out of range, such as an unrecognized `period`.                                                                                                                                                                        |
| `INVALID_CURSOR`             | 400    | A `cursor` value the API did not produce, on the reverse-lookup endpoints.                                                                                                                                                                  |
| `INVALID_REQUEST`            | 400    | Body is missing, not valid JSON, `wallets` is absent or empty, an `Idempotency-Key` header is over 200 characters, or a job id is not a UUID.                                                                                               |
| `INVALID_REQUEST`            | 413    | Request body exceeded the endpoint’s cap: 1 MB on batch and estimate, 4.5 MB on jobs.                                                                                                                                                       |
| `BATCH_SIZE_EXCEEDED`        | 400    | More wallets than your plan’s batch ceiling, on batch or estimate. 50 on the default plan; a live Scale or Index pack raises it (see [plans](/api-reference/introduction#plans)). The message names your ceiling.                           |
| `LIST_TOO_SMALL`             | 400    | An [estimate](/api-reference/estimate) with fewer than 10 distinct wallets. The counts are aggregates by design; for one address, resolve it.                                                                                               |
| `SUBMISSION_LIMIT_EXCEEDED`  | 402    | A [job](/api-reference/jobs) list longer than 10 times your match balance. The message names the maximum; shorten the list or top up.                                                                                                       |
| `JOB_ALREADY_ACTIVE`         | 409    | The account already has a pending or processing job, on any surface. The message names its id; poll it and resubmit when it completes.                                                                                                      |
| `JOB_NOT_FOUND`              | 404    | No job with this id is visible to this account. A job another account submitted answers exactly this.                                                                                                                                       |
| `NO_CREDITS`                 | 402    | Match credit balance is exhausted, or the free 30-day allowance is used up. Buy a pack, on either rail below. Only the metered endpoints return it; `/v1/stats`, `/v1/usage`, `/v1/estimate` and the job status poll answer at any balance. |
| `IDEMPOTENCY_KEY_REUSED`     | 422    | The same `Idempotency-Key` was sent with a different request body inside the 24-hour window. Use a fresh key for a new request. Batch only.                                                                                                 |
| `IDEMPOTENCY_NOT_REPLAYABLE` | 409    | The original response under this `Idempotency-Key` was too large to store, so it cannot be replayed. Resend under a fresh key; the resend bills as a new request. Batch only.                                                               |
| `SERVICE_UNAVAILABLE`        | 503    | Datastore is temporarily unreachable. Retry with backoff.                                                                                                                                                                                   |
| `INTERNAL_ERROR`             | 500    | Unexpected failure on our side.                                                                                                                                                                                                             |

## Running out of credits

Every metered endpoint checks your match balance before it runs. The free endpoints, [`/v1/stats`](/api-reference/stats), [`/v1/usage`](/api-reference/usage), [`/v1/estimate`](/api-reference/estimate) and the [job status poll](/api-reference/jobs#polling), skip the refusal and answer at any balance, so a drained key can always read its own meter, estimate its next list, and collect the results of a job it already paid for; that reading is the signal to top up. On a metered endpoint with nothing left, the call returns `402` with one of two `error` strings, depending on whether you are spending the free allowance or a pack:

```json theme={null}
{
  "error": "Free allowance used up for this 30-day window. Buy a pack at https://walletlink.social/pricing to continue, or buy one with USDC, no account needed, at POST https://walletlink.social/api/x402/buy.",
  "code": "NO_CREDITS"
}
```

```json theme={null}
{
  "error": "No credits left. Buy a pack at https://walletlink.social/pricing to continue, or buy one with USDC, no account needed, at POST https://walletlink.social/api/x402/buy.",
  "code": "NO_CREDITS"
}
```

The free allowance is 100 matches per rolling 30 days. A pack adds a balance that lasts 12 months, and the API draws on it exactly as the app does.

Both strings carry both remedies. A person buys a pack at [walletlink.social/pricing](https://walletlink.social/pricing); an agent holding a wallet buys credits with USDC over x402, no account, at `POST https://walletlink.social/api/x402/buy`. See [the agent pack](/agent-pack).

## Things that are not errors

Two cases return HTTP 200 and are easy to mistake for failures.

**A wallet with no identities.** `data: null`, `meta.found: false`. The lookup succeeded and the answer is that there is nothing attached. See [found, missing, and checked](/concepts/data-quality#found-missing-and-checked).

**A reverse lookup with no matches.** `data: []`, `meta.total_count: 0`.

## Retrying

`SERVICE_UNAVAILABLE` and `INTERNAL_ERROR` are worth retrying with exponential backoff. `RATE_LIMIT_EXCEEDED` is worth retrying once the window resets.

A retried request bills again, because a retry is a second resolution. The exception is a [batch](/api-reference/batch) resent with the same `Idempotency-Key`: inside the 24-hour window it replays the stored response, marked `Idempotency-Replayed: true`, and bills nothing. Send the header on any batch you might have to retry.

A [job](/api-reference/jobs) resubmission is refused with `JOB_ALREADY_ACTIVE` while the original runs, so a job cannot double-run by accident; resubmitted after completion, it runs the whole list again and bills again. Poll before resubmitting.

`NO_CREDITS` is not worth retrying. It clears when you buy a pack, or when the free window rolls forward.

Nothing in the `400` family is worth retrying. The request is malformed and will fail identically until you change it. `BATCH_SIZE_EXCEEDED` is the exception in spirit: split the batch and retry the pieces.

`401` means the key is wrong. Retrying will not fix it and repeated failures are a good way to look like an attack.
