> ## Documentation Index
> Fetch the complete documentation index at: https://supertone-b89202c8.mintlify.site/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.

**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)
```

**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;
  }
}
```

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

> **[Retries and backoff](/en/docs/production/retries-and-backoff)**
>
> The right retry policy for `429` and `5xx`.

> **[Rate limits](/en/docs/production/rate-limits)**
>
> Limits by account tier.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.