
# Errors

Every failure has the same shape:

```json
{
  "error": {
    "code": "quota_exceeded",
    "message": "The account is out of credits.",
    "retryable": false,
    "details": { "field": "limit" }
  },
  "meta": { "requestId": "req_01J…" }
}
```

**Branch on `retryable`**, not on a status-code list you maintain yourself.

`meta.requestId` is on every response and in the `x-request-id` header. Quote it in support
requests.

## Status codes

| Status | Code                  | Meaning                                      | Retry?                        |
| ------ | --------------------- | -------------------------------------------- | ----------------------------- |
| `400`  | `bad_request`         | Malformed. `details` names the field.        | No. Fix it.                   |
| `401`  | `unauthorized`        | Key missing, malformed, revoked, or expired. | No.                           |
| `402`  | `quota_exceeded`      | **Out of credits.**                          | **No.** See below.            |
| `403`  | `insufficient_scope`  | Key lacks a [scope](/authentication#scopes). | No. Issue a key with it.      |
| `404`  | `not_found`           | No such resource, or not public.             | No.                           |
| `409`  | `conflict`            | A limit would be exceeded, such as monitors. | No. Remove one first.         |
| `413`  | `bad_request`         | Body over the size limit.                    | No.                           |
| `429`  | `rate_limit_exceeded` | **Rate limited.**                            | **Yes**, after `Retry-After`. |
| `500`  | `internal_error`      | Our fault.                                   | Yes, with backoff.            |
| `503`  | `source_unavailable`  | A platform was unreachable.                  | Yes, with backoff.            |
| `504`  | `timeout`             | Outlived its deadline.                       | Yes, with backoff.            |

None of these costs credits. A refused or failed call is never charged.

## `402` and `429` are opposites

Both mean "not right now". Treating them the same is the most expensive mistake against this API.

**`429` is temporary.** You are going too fast. Wait for `Retry-After` and the same request works.

**`402` is not.** The account is out of credits. Only a person can clear it, by adding credits at
https://weirapp.io/dashboard/billing. Retrying never will. A retry loop on `402` runs until you
stop it and never succeeds. An agent that hits it should stop and tell its user.

`/v1/usage`, `/v1/limits`, and `/v1/whoami` stay reachable at `402`, so you can always find out why.

## Rate limits

Every metered response carries the current state:

```
ratelimit-limit: 30
ratelimit-remaining: 27
ratelimit-reset: 1800000060
```

| Header                | Meaning                                                   |
| --------------------- | --------------------------------------------------------- |
| `ratelimit-limit`     | Requests allowed per minute.                              |
| `ratelimit-remaining` | Requests left in the current window.                      |
| `ratelimit-reset`     | When the current one-minute window ends, in Unix seconds. |
| `Retry-After`         | On a `429` only: seconds to wait before trying again.     |

The limit is a rolling minute, so budget frees up gradually rather than all at once at
`ratelimit-reset`. Use `Retry-After` to decide when to try again.

Endpoints are weighted by how much upstream work they do. A search counts as 2 requests and a
comment tree as 4, while a single lookup counts as 1. Each operation's
[reference page](/reference/reddit/searchReddit) states its weight. It is the work being limited,
not the request count.

The free tier allows 30 requests a minute. Paid tiers allow more. `/v1/limits` returns the exact
figure for your account.

## When a platform is down

You get `503` with `source_unavailable`, and nothing is charged.

Each platform is its own endpoint, so one being down never affects the other. Fall back to the
other platform, or retry with backoff.
