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

# Errors and rate limits

> Hoppier API status codes, error bodies, safe retries with Idempotency-Key, and the two rate limits.

## Errors

Every error comes back as JSON with a `message` in plain words. Some errors also carry a machine-readable `error` code, and validation errors can name the field in `parameter`:

```json theme={null}
{ "message": "program prg_Z8q3NnW2xK not found" }
```

```json theme={null}
{
  "error": "rate_limited",
  "message": "Too many requests for this account. Retry after the number of seconds in Retry-After."
}
```

> [!NOTE]
>
> We're moving every error to one shape, `{ "error": "<code>", "message": "…" }`, with the usual statuses: 401 for a bad key, 403 for a missing permission, 402 for a missing subscription. This page will be updated the day that ships. Until then, act on the HTTP status, use `error` when it's there, and don't parse `message`.

### Status codes

| Status | Meaning | What to do |
| - | - | - |
| `200` | Success. | |
| `400` | Refused. Today this covers a missing or malformed key, a key whose owner isn't an admin (when listing programs), an account without an active subscription (when adding people), invalid fields, and a program that has ended. | Fix the request. Don't retry it unchanged. |
| `401` | The key is wrong, unknown or switched off, or its owner isn't an admin of the company or the program. | Check the key with [`GET /validate`](/api-reference/validate). |
| `402` | Your plan has no sends left (`code: send_credits_exhausted`). | Contact us to add more. |
| `404` | No such program in your company. A program in another company gets the same answer. | Check the id. |
| `409` | `request_outcome_unknown`: an earlier request with the same `Idempotency-Key` never finished. | Check whether the person was added, then retry with a new key. |
| `422` | `idempotency_key_reuse`: the same `Idempotency-Key` with a different body. | Use a new key for a new request. |
| `429` | Rate limited. | Wait `Retry-After` seconds, then retry. |
| `500` | Something went wrong on our side. | Retry with backoff. If it keeps happening, email [support@hoppier.com](mailto:support@hoppier.com). |
| `503` | `in_progress` (a request with the same `Idempotency-Key` is still running) or `rate_limit_unavailable`. | Wait `Retry-After` seconds, then retry. |

### Rough edges

A few errors don't look the way they should yet. Until the next API update:

* Adding a participant without `program` answers `500`.
* An invalid email address, or a first and last name longer than 24 characters together, answers `400` with a message that doesn't say which. Check both before you send them.
* On some older plans, running out of invitations answers `500` with a message that starts "Not enough remaining invitations".
* A `500` can carry internal error text. Log it, but don't show it to your users.
* The per-address rate limit answers in plain text, not JSON (see [Rate limits](#rate-limits)).

## Retrying safely

* Retry `429`, `503` and `5xx` responses with exponential backoff (for example 1, 2, 4, 8 seconds, up to a minute), and wait at least `Retry-After` when it's there. Don't retry other `4xx` responses unchanged.
* **When you add participants, always send an `Idempotency-Key`.** Generate a UUID for each add and reuse it on every retry of that add. Hoppier then adds the person once, however many times the request arrives. [How the key works](/api-reference/participants/add.md).
* Adding someone who's already in the program normally returns their existing record instead of adding them twice, but only an `Idempotency-Key` guarantees that a retry is safe.

## Rate limits

Two limits apply to every request:

| Limit | Allowance | Over the limit |
| - | - | - |
| Per IP address | 50 requests every 10 seconds | `429` in plain text (`Too Many Requests`). `Retry-After` is in seconds and can be a fraction today, so round it up. |
| Per account | 300 reads and 120 writes per minute | `429` JSON with `error: rate_limited`. `Retry-After` is in whole seconds. |

Reads are the `GET` requests and writes are the `POST` requests. All the keys in a company share the account limit, and Hoppier's Zapier app counts towards it too.

If Hoppier can't check the account limit, reads go through and writes answer `503` with `error: rate_limit_unavailable` and `Retry-After: 5`. Nothing was written, so retry.


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