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

# Error handling

> HTTP status codes returned by the Supertone API, their causes, and the right response in production code.

The Supertone API uses standard HTTP status codes. Errors return a JSON body describing the problem, and the SDKs surface them as typed exceptions you can catch and handle.

## Status code reference

| Status                       | Meaning                         | Most common cause                                                                                                |
| ---------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `200 OK`                     | Success                         | —                                                                                                                |
| `400 Bad Request`            | Malformed request               | Missing required field, invalid enum value, `text` over 300 chars (raw API only), `voice_settings` out of range. |
| `401 Unauthorized`           | Auth failure                    | Missing or invalid `x-sup-api-key`.                                                                              |
| `402 Payment Required`       | Not enough credits              | Credit balance is zero or insufficient for the request.                                                          |
| `403 Forbidden`              | No permission                   | Trying to call a custom voice owned by a different account, or using a key without permission.                   |
| `404 Not Found`              | Resource missing                | Wrong `voice_id`, mistyped endpoint path.                                                                        |
| `408 Request Timeout`        | Server couldn't process in time | Transient — retry.                                                                                               |
| `413 Payload Too Large`      | Upload too big                  | Voice-clone audio over 3 MB.                                                                                     |
| `415 Unsupported Media Type` | Bad upload format               | Voice-clone audio not WAV/MP3.                                                                                   |
| `429 Too Many Requests`      | Rate limit hit                  | See [Rate limits](/en/docs/production/rate-limits).                                                              |
| `500 Internal Server Error`  | Server-side issue               | Usually transient — retry with backoff.                                                                          |

## SDK error classes

Both SDKs map each status code to a typed error class. Catching the specific class is cleaner than parsing strings.

<Tabs>
  <Tab title="Python">
    ```python theme={"dark"}
    from supertone import errors

    try:
        response = client.text_to_speech.create_speech(...)
    except errors.BadRequestErrorResponse as e:
        # 400 — fix the request
        log.error("Bad request: %s", e.message)
    except errors.UnauthorizedErrorResponse:
        # 401 — re-check the API key
        rotate_key_and_retry()
    except errors.PaymentRequiredErrorResponse:
        # 402 — alert the user / billing system
        notify_low_credits()
    except errors.ForbiddenErrorResponse:
        # 403 — wrong voice for this account
        fallback_to_preset_voice()
    except errors.TooManyRequestsErrorResponse:
        # 429 — back off and retry
        backoff_and_retry()
    except errors.InternalServerErrorResponse:
        # 500 — retry transient failure
        retry_with_backoff()
    except errors.SupertoneError as e:
        # Any other API-level error
        log.exception("Supertone API error: %s", e.message)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"dark"}
    import * as errors from "@supertone/supertone/models/errors";

    try {
      const response = await client.textToSpeech.createSpeech({ /* ... */ });
    } catch (err) {
      if (err instanceof errors.BadRequestErrorResponse) {
        console.error("Bad request:", err.message);
      } else if (err instanceof errors.UnauthorizedErrorResponse) {
        rotateKeyAndRetry();
      } else if (err instanceof errors.PaymentRequiredErrorResponse) {
        notifyLowCredits();
      } else if (err instanceof errors.ForbiddenErrorResponse) {
        fallbackToPresetVoice();
      } else if (err instanceof errors.TooManyRequestsErrorResponse) {
        await backoffAndRetry();
      } else if (err instanceof errors.InternalServerErrorResponse) {
        await retryWithBackoff();
      } else if (err instanceof errors.SupertoneError) {
        console.error(`HTTP ${err.statusCode}: ${err.message}`);
      } else {
        throw err;
      }
    }
    ```
  </Tab>
</Tabs>

Every SDK error exposes:

| Property                       | Description                      |
| ------------------------------ | -------------------------------- |
| `message`                      | Server-provided error message.   |
| `status_code` / `statusCode`   | The HTTP status code.            |
| `headers`                      | Response headers.                |
| `body`                         | Raw response body string.        |
| `raw_response` / `rawResponse` | Underlying HTTP response object. |

## What to do with each error

### 4xx — your side

| Code  | Recommended response                                                                                                                 |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `400` | Don't retry — log the request body and fix the schema mismatch.                                                                      |
| `401` | Check key value, key activation in the console, and that the `x-sup-api-key` header isn't being stripped by a proxy.                 |
| `402` | Surface "insufficient credits" to the user, link to billing, and stop the workflow. Retrying without adding credits will fail again. |
| `403` | Confirm the voice belongs to this account. Custom voices are account-scoped.                                                         |
| `404` | Confirm the `voice_id` exists by calling `GET /v1/voices` or `GET /v1/custom-voices`.                                                |
| `413` | Reduce upload size — re-encode to mono, lower bitrate, or trim to under 3 MB.                                                        |
| `415` | Re-encode the upload as WAV or MP3.                                                                                                  |
| `429` | See [Retries and backoff](/en/docs/production/retries-and-backoff).                                                                  |

### 5xx — our side

| Code  | Recommended response                                                                                             |
| ----- | ---------------------------------------------------------------------------------------------------------------- |
| `408` | Retry — usually transient.                                                                                       |
| `500` | Retry with exponential backoff. If failures persist (>5 minutes), [contact support](/en/docs/resources/support). |

## Common pitfalls

* **`Content-Type: application/json` missing** on POST requests results in `400`.
* **API key with leading/trailing whitespace** (often from copy-paste) results in `401`.
* **Calling a custom voice from a different account** results in `403`, even if you "know" the ID.
* **Sending `text` over 300 characters** to the raw API results in `400`. Use an SDK to auto-chunk longer text.

## Related

<CardGroup cols={2}>
  <Card title="Retries and backoff" icon="rotate" href="/en/docs/production/retries-and-backoff">
    The right retry policy for `429` and `5xx`.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/en/docs/production/rate-limits">
    Limits by account tier.
  </Card>
</CardGroup>
