# Errors and rate limits

HeadHoncho API errors share one envelope and name their reason, and every call tells you how many calls a minute you have left.

Page: https://headhoncho.app/docs/api/errors | Part of the HeadHoncho API docs: https://headhoncho.app/docs/api | Machine-readable: https://headhoncho.app/docs/api/openapi.json

## The error envelope

HeadHoncho API errors all have one shape. The HTTP status says the class of refusal, and the body names the reason:

```json
{
  "error": {
    "code": "invalid",
    "message": "Some fields were refused. See fields.",
    "fields": {
      "client_phone": "This is required."
    },
    "docs": "https://headhoncho.app/docs/api/errors#invalid"
  }
}
```

- `code` is the stable name to branch on.
- `message` is the sentence to show a person.
- `fields` names each refused field, on a 422 only.
- `docs` links to the entry for the code on this page.

The MCP server prints the same code. A refusal always names its reason.

## Every error code

Each code is an anchor, so `https://headhoncho.app/docs/api/errors#invalid` goes straight to its entry.

### missing_key

**Status 401.** Send your key as Authorization: Bearer hh_live_... Every endpoint can return it.

### unknown_key

**Status 401.** That key is not one we issued, or it was revoked. Every endpoint can return it.

### expired_token

**Status 401.** That token has expired. Refresh it, or connect the app again. Every endpoint can return it.

### no_access

**Status 403.** This credential does not reach that. Ask the person who made it for access. Returned by 15 of the 16 endpoints.

### read_only

**Status 403.** This credential may read this area but not change it. Returned by 6 of the 16 endpoints.

### not_found

**Status 404.** No such record in the companies this credential reaches. Returned by 15 of the 16 endpoints.

### out_of_scope

**Status 404.** No such company in the companies this credential reaches. Returned by 7 of the 16 endpoints.

### unknown_endpoint

**Status 404.** No such endpoint. GET /api/v1/ lists every one. Returned by 0 of the 16 endpoints.

### unknown_command

**Status 404.** No such command. GET /api/v1/ lists every one. Returned by 0 of the 16 endpoints.

### invalid

**Status 422.** Some fields were refused. See fields. Returned by 15 of the 16 endpoints.

### missing_idempotency_key

**Status 400.** Send an Idempotency-Key header on every command. Returned by 6 of the 16 endpoints.

### idempotency_mismatch

**Status 409.** This Idempotency-Key was used with a different body. Returned by 6 of the 16 endpoints.

### rate_limited

**Status 429.** Too many calls this minute. Wait and try again. Every endpoint can return it.

### not_connected

**Status 409.** This company has not connected that provider yet. Returned by 3 of the 16 endpoints.

## Rate limits

Every call counts against the allowance of the key or connected app that made it. The allowance is per minute and depends on the cost of the call:

| Cost | Calls a minute | Endpoints |
| --- | --- | --- |
| **read** | 300 | [Whoami](https://headhoncho.app/docs/api/whoami), [List jobs](https://headhoncho.app/docs/api/list_jobs), [Get job](https://headhoncho.app/docs/api/get_job), [Get job history](https://headhoncho.app/docs/api/get_job_history), [List tables](https://headhoncho.app/docs/api/list_tables), [Read table](https://headhoncho.app/docs/api/read_table), [Get job conversion](https://headhoncho.app/docs/api/get_job_conversion), [Get click id report](https://headhoncho.app/docs/api/get_click_id_report) |
| **write** | 60 | [Create job](https://headhoncho.app/docs/api/create_job), [Update job](https://headhoncho.app/docs/api/update_job), [Change job status](https://headhoncho.app/docs/api/change_job_status), [Assign worker](https://headhoncho.app/docs/api/assign_worker), [Unassign worker](https://headhoncho.app/docs/api/unassign_worker), [Add job note](https://headhoncho.app/docs/api/add_job_note) |
| **export** | 10 | [Export table](https://headhoncho.app/docs/api/export_table), [Export click id report](https://headhoncho.app/docs/api/export_click_id_report) |

Every answer carries the headers that tell you where you stand:

- `X-RateLimit-Limit`: the calls a minute this credential may make at this cost.
- `X-RateLimit-Remaining`: how many are left in the current minute.
- `Retry-After`: on a 429, the seconds to wait before trying again.

Over the limit, the call is refused with status 429 and [`rate_limited`](#rate_limited). Wait for `Retry-After` and send it again. A command is safe to resend with the same `Idempotency-Key`; see [the model](https://headhoncho.app/docs/api/model#idempotency). Keys and their access are on [API keys](https://headhoncho.app/docs/api/keys).
