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

# Percify API errors and rate limits

> Percify API status codes, error body format and every limit: 60 requests a minute per key, 60 generation starts a minute per account, monthly key caps.

The Percify API answers errors with a standard HTTP status and a short message that says what went wrong, such as `Missing required field: audio` or `Not enough credits`. Each API key can make 60 requests a minute, each account can start 60 generations a minute, and a key can also have a monthly credit cap. Requests refused for any of these reasons are not charged.

## Error body

Most errors return a JSON object:

```json theme={"system"}
{
  "statusCode": 400,
  "message": "Missing required field: prompt",
  "error": "Bad Request"
}
```

Rate limit errors (`429`) return the message as a plain JSON string instead:

```json theme={"system"}
"API key monthly credit limit reached (500 credits)."
```

Handle both: if the parsed body is a string, it is the message; otherwise read `message`.

## Status codes

| Status        | When                                                                                    | Retry?                                      |
| ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------- |
| `200` / `201` | Success. The result is in `data`.                                                       |                                             |
| `400`         | The request is invalid, content was rejected by moderation, or your balance is too low. | Only after fixing the request or topping up |
| `401`         | The API key is missing, wrong, expired or revoked.                                      | No, fix the key                             |
| `403`         | The account is suspended: `Your account has been suspended.`                            | No, contact support                         |
| `404`         | The generation, job or model does not exist for your account.                           | No                                          |
| `429`         | A rate limit or the key's monthly credit cap was reached.                               | Yes, after waiting                          |
| `500`         | An unexpected server error.                                                             | Yes, with backoff                           |
| `503`         | The model is at capacity. Any credits were refunded.                                    | Yes, with backoff                           |

Check `response.ok` (any `2xx`) rather than one exact success code.

## Common error messages

| Status | Message starts with                                                 | Fix                                                                                              |
| ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `400`  | `Unknown model '…'. Available models: …`                            | Use an id from [`GET /v1/models`](/api-reference/models).                                        |
| `400`  | `input must be an object`                                           | Send `input` as a JSON object.                                                                   |
| `400`  | `Missing required field: …`                                         | Add the named field.                                                                             |
| `400`  | `<field> must be one of: …`                                         | Use one of the listed values.                                                                    |
| `400`  | `audio URL is not reachable`                                        | Host the file on a public `https` URL.                                                           |
| `400`  | `Unsupported media format`                                          | Use `.mp3`, `.wav` or `.mp4`.                                                                    |
| `400`  | `Could not read the video duration`                                 | Re-encode the MP4 as a standard file with its index at the start (faststart).                    |
| `400`  | `Prompt rejected by content moderation`                             | Change the text. Nothing was charged.                                                            |
| `400`  | `Image rejected by content moderation`                              | Use a different image. Nothing was charged.                                                      |
| `400`  | `Not enough credits`                                                | The message gives the credits needed and your balance. [Top up](https://app.percify.io/billing). |
| `400`  | `webhook must use https`, `webhook host is not allowed` and similar | Use a public `https` webhook URL.                                                                |
| `400`  | `Invalid generation id`                                             | Pass the UUID returned by `/v1/run`.                                                             |
| `401`  | `Missing credentials. Use: Authorization: Bearer pk_live_...`       | Send the `Authorization` header.                                                                 |
| `401`  | `Invalid, expired, or revoked API key`                              | Create a new key on the [developer page](https://app.percify.io/home/developer).                 |
| `404`  | `Generation not found`                                              | The id belongs to another account or does not exist.                                             |
| `404`  | `Model not found. Available models: …` or `Model '…' not found`     | Use an id from `GET /v1/models`.                                                                 |
| `429`  | `API key rate limit exceeded`                                       | Slow down to 60 requests a minute on this key.                                                   |
| `429`  | `Too many generations`                                              | Slow down to 60 generation starts a minute on this account.                                      |
| `429`  | `API key monthly credit limit reached (N credits).`                 | Wait for the next calendar month, or use a key with a higher cap.                                |
| `503`  | `The model is at capacity`                                          | Retry after a short wait.                                                                        |

A generation can also start fine and fail later. Then `GET /v1/generations/{id}` returns `status: "failed"` with the reason in `error`, for example `Generation timed out`, and the credits are refunded.

## Rate limits

| Limit                           | Scope                         | Applies to                                             |
| ------------------------------- | ----------------------------- | ------------------------------------------------------ |
| 60 requests per minute          | Each API key                  | Every authenticated request: runs, polls and estimates |
| 60 requests per minute          | Each OAuth access token       | MCP clients signed in with OAuth                       |
| 60 generation starts per minute | Each account, across all keys | `POST /v1/run` and runs started by MCP tools           |
| **Monthly credit cap**          | Each API key, when set        | Credits a key can spend in a calendar month            |

The limits use a rolling 60-second window. Responses do not include `Retry-After` or rate limit headers, so after a `429` wait about a minute before sending more requests on that key or account. The public catalog endpoints (`/v1/models`, `/v1/openapi.json`, `/v1/llms.txt`) need no key and do not count toward a key's limit.

## Retry strategy

1. Retry `429`, `500` and `503` with exponential backoff, starting around 5 seconds and waiting about 60 seconds after a `429`.
2. Do not retry `400`, `401`, `403` or `404` unchanged.
3. Send the same `Idempotency-Key` on every retry of `POST /v1/run`, so a request that did start is returned instead of charged twice.
4. Poll with `GET /v1/generations/{id}?wait=45` instead of many short polls, to stay under 60 requests a minute.

## When to contact support

Email [support@percify.io](mailto:support@percify.io) if you get `403 Your account has been suspended.`, if `500` errors continue after retries, or if a failed generation's credits did not come back. Include the generation id and the time of the request.

## Related

<CardGroup cols={2}>
  <Card title="Authentication" href="/percify/api-auth">
    Fix 401 errors and manage keys.
  </Card>

  <Card title="Async jobs and polling" href="/api-reference/async-jobs">
    Statuses, timeouts and refunds.
  </Card>

  <Card title="Credits and usage" href="/api-reference/user/overview">
    Balance, spend and key caps.
  </Card>

  <Card title="Code examples" href="/guides/sdk-integration">
    Error handling in Node.js and Python.
  </Card>
</CardGroup>
