# Rate limits and errors

How many calls you can make, what an error looks like and what each one means.

Source: https://citypicked.com/en/developers/errors

## Rate limits

| What | Per key | Per account |
| --- | --- | --- |
| Reads (every `GET`) | 120 a minute | 600 a minute |
| Sends (`POST /v1/requests`, cancel, adding or removing webhooks) | 30 a minute | 60 a minute |
| New keys in the app |  | 20 an hour |

- Up to 100 contacts per `POST /v1/requests`, and a body of at most 256 KB
- `send_at` plans up to 30 days ahead
- Over a limit the answer is 429 with `Retry-After` in seconds

## Error format

Every error is an HTTP status plus a JSON body. `code` is stable; `message` is for people and may change.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Each contact needs an email address for channel email.",
    "field": "contacts.0.email"
  }
}
```

## Error codes

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | The query or body is not valid. `field` names the wrong one, like `contacts.2.email` |
| 401 | `unauthorized` | No key, or the key does not work any more |
| 402 | `plan_wall` | The account cannot send right now because the trial is over or the plan has no access. Reading keeps working |
| 403 | `forbidden` | The key lacks the `send` scope, or the plan has no API sending |
| 403 | `account_closed` | The account is closed. Every endpoint answers this |
| 404 | `not_found` | Nothing with this id for this key |
| 409 | `idempotency_conflict` | The `Idempotency-Key` was used before with another body |
| 409 | `not_cancelable` | The request already went out |
| 429 | `rate_limited` | Too many calls. Wait for the seconds in `Retry-After` |
| 500 | `server_error` | Something went wrong on our side. Try again; with an `Idempotency-Key` nothing is sent twice |

## Retrying safely

- Retry 429 after `Retry-After`, and 500 or a network error after a short wait
- Send an `Idempotency-Key` with `POST /v1/requests`. The same key within 24 hours returns the first answer and sends nothing again
- Never retry 400, 401, 403 or 404 unchanged

A send where some contacts could not go out still answers 201; each contact has its own `outcome` ([quickstart](https://citypicked.com/en/developers/quickstart)).
