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

# Errors

> One error shape, ten codes, and which ones are worth retrying.

Every failure returns the same envelope:

```json theme={null}
{
  "error": {
    "code": "invalid_request",
    "message": "Invalid request",
    "details": [
      { "path": "brandNames", "message": "brandNames is required for mode \"brand_name\"." }
    ]
  }
}
```

`code` is stable and safe to branch on. `message` is written for humans and may
change — don't match on it. `details` appears only on validation failures, and
lists each offending field.

## Codes

| Status | Code                    | What happened                                                           |
| ------ | ----------------------- | ----------------------------------------------------------------------- |
| 400    | `invalid_request`       | The body or query string failed validation. `details` names the fields. |
| 401    | `invalid_api_key`       | The key is missing, malformed, unknown, or revoked.                     |
| 402    | `out_of_credits`        | The team's monthly credits are exhausted.                               |
| 403    | `membership_ended`      | You're no longer a member of the key's team.                            |
| 403    | `subscription_inactive` | The team's subscription isn't active.                                   |
| 404    | `brand_not_found`       | No brand matches that id.                                               |
| 405    | `method_not_allowed`    | `HEAD` on a metered endpoint. Use `GET`.                                |
| 413    | `payload_too_large`     | The request body exceeds 100 KB.                                        |
| 429    | `rate_limited`          | A rate limit was exceeded. Carries `Retry-After`.                       |
| 500    | `internal_error`        | Something broke on our side.                                            |

## What to retry

**Retry `429`** after the number of seconds in the `Retry-After` header. Don't
retry sooner — it counts against the same window.

**Retry `500`** once or twice with backoff. Nothing was billed, so a retry can't
double-charge.

**Don't retry anything else.** `400`, `401`, `403`, `404`, `405` and `413` will
fail identically until you change the request, the key, or the plan. `402` will
keep failing until credits are added — retrying in a loop just wastes calls
against your rate limit.

## None of these are billed

Every error above is free. You're only charged for rows actually returned, so a
failed call never draws credits — including `500`.

## Distinguishing the two 403s

Both mean "your access is gone", but the fix differs:

* `membership_ended` — *you* were removed from the team. Create a key from a
  team you're still in.
* `subscription_inactive` — the *team's* plan lapsed. Someone with billing
  access needs to renew.
