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

# Error handling

> Catch and respond to invalid input, auth problems, rate limits, and more.

Every API error returns the same JSON shape, so you can handle all of them the same
way: check the status code, read the stable `error.code`, and branch on it. The
human `message` is for display and logging only — never parse it, because the
wording can change.

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_field",
    "message": "Field required",
    "param": "caption",
    "doc_url": "https://docs.mymarky.ai/errors#missing_field"
  }
}
```

## The error object

| Field        | Description                                                                                                                              |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `type`       | The broad category of the error (see [Error types](#error-types) below). Derived from the HTTP status.                                   |
| `code`       | A stable, machine-readable identifier. **Match on this.** Every value is listed in the [Error codes](/errors) reference.                 |
| `message`    | A human-readable explanation. For display and logging only — do not parse it.                                                            |
| `param`      | The request field that caused the error, when the error is field-specific.                                                               |
| `doc_url`    | A link to this error's entry in the [Error codes](/errors) reference.                                                                    |
| `details`    | On validation errors with more than one bad field, an array of `{ param, message }` for every field that failed.                         |
| `request_id` | The id of the request that failed (also on the `X-Request-Id` header). Quote it to support so we can find the exact request in our logs. |

## Request IDs

Every response — success or error — includes an `X-Request-Id` header (for example
`req_4f3c…`). On errors it's also in the body as `error.request_id`. Log it on your
side, and include it when you contact support so we can find the exact request fast.

```bash theme={null}
curl -i https://api.mymarky.ai/api/businesses \
  -H "Authorization: Bearer mk_live_your_key"
# ...
# X-Request-Id: req_4f3c9a1b2c3d4e5f6a7b8c9d
```

## Catch errors

Check for a non-2xx status, read `error.code`, and decide what to do.

<CodeGroup>
  ```python Python theme={null}
  import httpx

  resp = httpx.post(
      "https://api.mymarky.ai/api/businesses/{business_id}/posts",
      headers={"Authorization": "Bearer mk_live_your_key_here"},
      json={...},
  )

  if resp.status_code >= 400:
      error = resp.json()["error"]
      code = error["code"]

      if code == "rate_limit_exceeded":
          # Back off and retry — see "Handle rate limits" below.
          ...
      elif code == "INSUFFICIENT_CREDITS":
          # error["required_credits"] tells you how many more are needed.
          ...
      elif code in ("validation_error", "missing_field", "invalid_value"):
          # Fix the request. error["param"] names the offending field.
          ...
      else:
          # Log code + message + the request id from the response headers.
          print(error["code"], error["message"])
  ```

  ```javascript Node.js theme={null}
  const resp = await fetch("https://api.mymarky.ai/api/businesses/{business_id}/posts", {
    method: "POST",
    headers: { Authorization: "Bearer mk_live_your_key_here" },
    body: JSON.stringify({ ... }),
  });

  if (!resp.ok) {
    const { error } = await resp.json();

    switch (error.code) {
      case "rate_limit_exceeded":
        // Back off and retry — see "Handle rate limits" below.
        break;
      case "INSUFFICIENT_CREDITS":
        // error.required_credits tells you how many more are needed.
        break;
      case "validation_error":
      case "missing_field":
      case "invalid_value":
        // Fix the request. error.param names the offending field.
        break;
      default:
        console.error(error.code, error.message);
    }
  }
  ```
</CodeGroup>

## Error types

`type` groups errors by what generally went wrong, so you can pick a broad response
without listing every code. For the specific reason, use `code`.

| Type                    | HTTP status        | What it means                                               | How to respond                                                                          |
| ----------------------- | ------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `authentication_error`  | 401                | Missing or invalid credentials.                             | Send a valid API key. Reconnect a social account if the code is `SOCIAL_TOKEN_EXPIRED`. |
| `permission_error`      | 403                | Valid key, but no access to this resource.                  | Check the resource belongs to your org and your plan includes API access.               |
| `not_found_error`       | 404                | The resource doesn't exist or isn't yours.                  | Check the ID. Don't retry without changing it.                                          |
| `invalid_request_error` | 400, 409, 413, 422 | Bad input or a request that can't run in the current state. | Fix the request using `code` and `param`. Don't retry unchanged.                        |
| `rate_limit_error`      | 429                | Too many requests.                                          | Back off and retry (see below).                                                         |
| `api_error`             | 500, 502           | A problem on our end or with an upstream provider.          | Treat as transient and retry with backoff.                                              |

## Handle rate limits

When you get `rate_limit_exceeded` (429), wait and retry. Prefer the `Retry-After`
header when it's present; otherwise back off exponentially.

```python theme={null}
import time

if resp.status_code == 429:
    delay = int(resp.headers.get("Retry-After", 1))
    time.sleep(delay)
    # retry the request
```

## Handle transient errors

`internal_error` (500) and `UPSTREAM_PROVIDER_ERROR` (502) are usually temporary and
not a problem with your request. Retry with exponential backoff. For writes, retry
the same request rather than creating a new one, so you don't duplicate work.

For anything in the `invalid_request_error` family, do **not** blindly retry — the
request needs to change first. Use `code` and `param` to fix it.

## Next steps

See the full [Error codes](/errors) reference for every `code`, what it means, and
how to resolve it. Each error's `doc_url` links straight to its entry there.
