# Errors

> The error envelope, the stable code vocabulary, and what to branch on (and what never to parse).

Every error response — any status other than `200`/`202` — carries one JSON shape, wrapped under
`error`:

```json
{
  "error": {
    "code": "not-found",
    "message": "no creator found for instagram/somehandle",
    "details": { "reason": "account-not-discoverable", "retryable": true }
  }
}
```

| Field | Contract |
| --- | --- |
| `error.code` | **Stable and machine-readable.** Branch on this. Maps 1:1 to the HTTP status. |
| `error.message` | Human prose for logs and error screens. **Never parse it** — wording can change any time. |
| `error.details` | Optional structured extras. Today's one use: a read-miss `404` carries `error.details.reason`. |
| `error.details.retryable` | Only present on `error.details`, and only `true` — never `false` or explicitly absent-as-false. **Present and `true`** means the miss was on our side, not a fact about the entity: retry after a few minutes. **Absent** means terminal: stop retrying. |

## The codes

| `code` | HTTP | When |
| --- | --- | --- |
| `unauthorized` | 401 | Missing, malformed, expired, or revoked credential. |
| `forbidden` | 403 | Valid credential, but it lacks the `<resource>:<action>` permission this endpoint requires. |
| `not-found` | 404 | Single reads: a read miss (check `error.details.reason` — retry only if `error.details.retryable` is `true`), an unknown `asProfile`, or an unsupported network. |
| `invalid-input` | 400 | A malformed request — e.g. a batch over the 100-identifier cap, or an unparseable identifier. |
| `rate-limited` | 429 | Request budget exceeded. Carries a `Retry-After` header — see [rate limits](/docs/reference/rate-limits/). |
| `internal-error` | 500 | Server-side fault. The message is deliberately generic; internal error detail is never exposed. Safe to retry with backoff. |
| `unavailable` | 503 | A dependency required to verify the credential is unreachable; the credential was neither accepted nor rejected. Safe to retry with backoff. |

Evolution across this whole API is additive-only: a response may gain a new field, and any open
vocabulary (like the code set above) may gain a new value, at any time. Never reject an unknown
field or an unrecognized value you don't branch on — implement a `default` case instead.

A `202 fetching` response and a batch item landing in `fetching` or `unavailable` are not errors —
these are ordinary read outcomes with their own contract.

## Contacting support

Include the `x-request-id` response header of the failing call. It is present on every response
and identifies the specific request.
