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

# Errors & rate limits

> Status codes, the credit model, and how to recover.

Every error response is JSON with at least an `error` field, and often a `message` for humans:

```json theme={"system"}
{
  "error": "insufficient_credits",
  "message": "Not enough credits to start a render. Top up at /developer/billing."
}
```

## Status codes

| Status | Meaning                                                                                                                  | What to do                                                                                                                          |
| ------ | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Request body or parameters invalid (bad `className`, unknown `quality`, non-`https://` `webhookUrl`, malformed JSON, …). | Read `error`; fix the field it names. These are deterministic — don't retry without changing the request.                           |
| `401`  | Missing, malformed, revoked, or unknown API key.                                                                         | Check that `Authorization: Bearer kdsc_live_…` is set and matches a live key in the [dashboard](https://kodisc.com/developer/keys). |
| `402`  | Not enough credits to start the render.                                                                                  | Top up from [billing](https://kodisc.com/developer/billing) or wait until your monthly free allotment resets.                       |
| `404`  | The `jobId` doesn't exist or doesn't belong to this key's user.                                                          | Confirm the ID; jobs are scoped to the user that created them.                                                                      |
| `5xx`  | Transient server-side failure.                                                                                           | Retry with exponential backoff.                                                                                                     |

A render that *fails* during execution (bad Manim code, runtime error) still returns `200` from `GET /api/v2/render/{jobId}` — the failure surfaces as `status: "failed"` with `error` populated. HTTP `5xx` is reserved for Kodisc-side problems, not your scene's.

## The credit model

Credits are the unit Kodisc bills against. Each render consumes credits proportional to its wall-clock render time and quality preset.

The lifecycle of credits during a render:

<Steps>
  <Step title="Reserve">
    On `POST /api/v2/render`, Kodisc reserves a minimum amount of credits up-front. If your balance is below the reserve, the call fails with `402` immediately — no job is created.
  </Step>

  <Step title="Settle">
    When the render finishes, Kodisc computes the actual cost from `durationMs` and `quality`. If the actual cost exceeds the reserve, the difference is charged. If you don't have enough to cover it, the job is marked `failed` and the reserve is refunded.
  </Step>

  <Step title="Refund">
    If the actual cost is below the reserve, the difference is automatically returned to your balance. Failed jobs refund the full reserve.
  </Step>
</Steps>

Credit balance is visible at any time via [`GET /api/v2/me`](/api-reference/endpoint/get-me) — the response splits **paid** (purchased credits) from **free** (your plan's monthly allowance).

## Common 400 messages

| `error`                                                  | Cause                                                  |
| -------------------------------------------------------- | ------------------------------------------------------ |
| `JSON body required`                                     | Body wasn't valid JSON.                                |
| `code is required`                                       | `code` was missing or empty.                           |
| `className must be a valid Python identifier`            | `className` didn't match `^[A-Za-z_][A-Za-z0-9_]*$`.   |
| `quality must be one of: low, medium, high, twok, fourk` | Unknown `quality`.                                     |
| `aspectRatio must be one of: 16:9, 9:16`                 | Unknown `aspectRatio`.                                 |
| `fps must be a positive integer`                         | `fps` wasn't a positive integer.                       |
| `webhookUrl must be an https:// URL`                     | `webhookUrl` was set but didn't start with `https://`. |

## Retry strategy

* `400` and `401` are **client errors** — retrying without changes won't help.
* `402` clears as soon as your balance increases; retry after topping up or after the next free-credit refresh.
* `404` is permanent for the given `jobId`.
* `5xx` and network errors are safe to retry; use exponential backoff (e.g. 1s → 2s → 4s, up to 5 attempts).
