# Errors

> Problem details, stable codes and what to do about each one.

Errors use [RFC 9457 problem details](https://www.rfc-editor.org/rfc/rfc9457) with `Content-Type: application/problem+json`:

```json
{
  "type": "about:blank",
  "title": "Key lacks a permission",
  "status": 403,
  "detail": "This key needs the posts:publish permission for this endpoint.",
  "code": "insufficient_scope",
  "request_id": "req_f3T0p9vK2xQm1aLs",
  "required_scope": "posts:publish"
}
```

Branch on `code`, never on `title` or `detail`: codes are stable, the texts may improve. Validation errors list each problem in `errors`, with a `parameter` (query) or a `pointer` (body, such as `/targets/0/account_id`).

## Codes

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_cursor` | 400 | Invalid cursor |
| `invalid_idempotency_key` | 400 | Invalid Idempotency-Key |
| `invalid_request` | 400 | Invalid request |
| `invalid_webhook_url` | 400 | Invalid webhook URL |
| `tiktok_consent_required` | 400 | TikTok consent required |
| `api_key_expired` | 401 | API key expired |
| `api_key_revoked` | 401 | API key revoked |
| `invalid_api_key` | 401 | Invalid API key |
| `missing_api_key` | 401 | API key required |
| `key_spend_cap_reached` | 402 | Key Token cap reached |
| `max_tokens_exceeded` | 402 | Price above max_tokens |
| `max_tokens_required` | 402 | max_tokens required |
| `plan_required` | 402 | Plan does not include API access |
| `workspace_locked` | 402 | Workspace locked |
| `comment_custodian_required` | 403 | Only the account's custodian can act on its comments |
| `developer_platform_unavailable` | 403 | API access not enabled |
| `insufficient_scope` | 403 | Key lacks a permission |
| `ip_not_allowed` | 403 | IP address not allowed |
| `network_locked` | 403 | Network not included in the plan |
| `network_unavailable` | 403 | Network not available |
| `permission_denied` | 403 | Not allowed |
| `personal_keys_disabled` | 403 | Personal keys are turned off |
| `test_key_required` | 403 | Test key required |
| `workspace_key_required` | 403 | Workspace key required |
| `workspace_unavailable` | 403 | Workspace unavailable |
| `not_found` | 404 | Resource not found |
| `unknown_endpoint` | 404 | Not found |
| `method_not_allowed` | 405 | Method not allowed |
| `account_limit_reached` | 409 | Account limit reached for this network |
| `comment_draft_pending` | 409 | A reply draft is waiting |
| `conversation_changed` | 409 | Conversation changed |
| `idempotency_in_flight` | 409 | Request with this Idempotency-Key in progress |
| `simulated_account` | 409 | Not available for test accounts |
| `webhook_endpoint_limit_reached` | 409 | Webhook endpoint limit reached |
| `idempotency_key_reused` | 422 | Idempotency-Key used for another request |
| `project_restricted` | 423 | Project restricted |
| `workspace_read_only` | 423 | Workspace is read-only |
| `comment_limit_reached` | 429 | Daily comment limit reached |
| `rate_limited` | 429 | Too many requests |
| `server_error` | 500 | Server error |
| `provider_error` | 502 | The network refused or failed |
| `service_unavailable` | 503 | Service unavailable |

Actions on posts, media and accounts can also answer the same codes as the app, such as `past_schedule`, `video_required`, `plan_limit_video_size` or `publication_limit` (a plan limit, with the details in `limits`). They keep the HTTP status that fits: `400` for a request to fix, `402` for the plan or balance, `409` for a conflict with the current state.

## Retrying

Retry `429` after `Retry-After` seconds, and `500`, `502` and `503` with exponential backoff. Send the same `Idempotency-Key` so a retry never repeats a change. Do not retry other `4xx` errors without changing the request.

A `500` carries a `reference`. Send it with the `Request-Id` when you contact support.
