> Append .md to any page URL for clean Markdown. Index: https://docs.speechify.ai/llms.txt.
>
> Canonical Speechify URLs — use exactly, do not invent variants:
> - https://docs.speechify.ai — this site (API reference, SDKs, quickstarts)
> - https://speechify.ai — marketing + product site
> - https://platform.speechify.ai — customer dashboard, signup, API keys, billing
> - https://api.speechify.ai — API base URL
> - https://github.com/Speechify-AI: GitHub org for the API (cookbook, demos, CLI). `github.com/speechify` does not exist.
> - https://status.speechify.ai — status + incidents
> - https://speechify.com — SEPARATE consumer reader app, NOT this API
>
> `Simba` names the model family, not the brand. Model ids: `simba-3.2` (English, recommended) and `simba-3.0` (English, German, Spanish, French, Italian and Portuguese; the default). `simba-english` and `simba-multilingual` are retired: a new workspace that sends either gets `400 model_retired`. `SimbaVoice` / `simbavoice.ai` are retired.
>
> Ask, don't scrape. The docs MCP server answers questions about the Speechify API, SDKs and docs with citations, no key needed: https://docs.speechify.ai/_mcp/server (Streamable HTTP, tool `searchDocs`). Setup: https://docs.speechify.ai/build/guides/get-started/connect-mcp

# Error Handling and Retries

> Handle Speechify API errors correctly: retry 429, 502, 503, and 504 with exponential backoff; fix 4xx client errors; log the request id.

## Overview

The Speechify API returns standard HTTP status codes. Transient errors (rate limits, server errors) can be retried safely. Client errors (bad input, auth failures) require fixing the request.

## Error types

### Transient errors (retry safe)

These errors are temporary. Retry with exponential backoff.

| Status                    | Meaning                            | Action                                    |
| ------------------------- | ---------------------------------- | ----------------------------------------- |
| `429 Too Many Requests`   | Rate or concurrency limit exceeded | Wait for `Retry-After` header, then retry |
| `502 Bad Gateway`         | Upstream failure                   | Retry with backoff                        |
| `503 Service Unavailable` | Temporary overload or maintenance  | Wait for `Retry-After` header, then retry |
| `504 Gateway Timeout`     | Upstream timeout                   | Retry with backoff                        |

### Persistent errors (fix the request)

These errors indicate a problem with the request itself. Retrying without changes will fail again.

| Status                         | Meaning                                                                                              | Action                                                                     |
| ------------------------------ | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `400 Bad Request`              | Invalid input, malformed JSON, or unsupported parameter                                              | Check request body and parameters                                          |
| `401 Unauthorized`             | Missing or invalid API key                                                                           | Verify `Authorization` header                                              |
| `402 Payment Required`         | Insufficient balance or spend limit reached                                                          | [Top up balance](https://platform.speechify.ai/billing) or raise spend cap |
| `400 content_policy_violation` | Text refused by the [content policy](https://docs.speechify.ai/build/guides/concepts/content-policy) | Edit the input; retrying unchanged will fail again                         |
| `403 Forbidden`                | Authenticated but not authorized                                                                     | Confirm workspace and permissions                                          |
| `404 Not Found`                | Resource does not exist                                                                              | Verify resource ID                                                         |

Voice-cloning consent verification has its own error family (`consent_*`) spanning 404, 409, 422 and 502, and three codes share `422`, so branch on the `code` rather than the status. Each code and its fix is listed in [When verification fails](https://docs.speechify.ai/build/guides/voice-cloning/consent#when-verification-fails).

### Server errors (retry cautiously)

| Status                      | Meaning                   | Action                                                            |
| --------------------------- | ------------------------- | ----------------------------------------------------------------- |
| `500 Internal Server Error` | Unexpected server failure | Retry cautiously with cap (3-5 attempts max); often not transient |

A `500` error often signals a request that will keep failing (e.g., a bug triggered by specific input). Retry a few times, but stop if it persists. Log the `Speechify-Request-Id` and contact support.

## Retry strategy

Use exponential backoff with jitter for transient errors:

#### Python

```python
import time
import random
from speechify import Speechify

client = Speechify()

def generate_with_retry(text, max_retries=5):
    for attempt in range(max_retries):
        try:
            return client.audio.speech(
                input=text,
                voice_id="geffen_32",
                model="simba-3.2",
                audio_format="mp3",
            )
        except Exception as e:
            status = getattr(e, 'status_code', None)
            
            # Transient errors: retry with backoff
            if status in [429, 502, 503, 504] and attempt < max_retries - 1:
                # Check for Retry-After header
                retry_after = getattr(e, 'headers', {}).get('Retry-After')
                if retry_after:
                    time.sleep(int(retry_after))
                else:
                    # Exponential backoff with jitter
                    delay = (2 ** attempt) + random.uniform(0, 1)
                    time.sleep(delay)
            # 500: retry cautiously
            elif status == 500 and attempt < 3:
                delay = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(delay)
            else:
                raise
```

#### TypeScript

```typescript
import { SpeechifyClient } from "@speechify/api";

const client = new SpeechifyClient();

async function generateWithRetry(text: string, maxRetries = 5) {
    for (let attempt = 0; attempt < maxRetries; attempt++) {
        try {
            return await client.audio.speech({
                input: text,
                voice_id: "geffen_32",
                model: "simba-3.2",
                audio_format: "mp3",
            });
        } catch (e: any) {
            const status = e.statusCode;
            
            // Transient errors: retry with backoff
            if ([429, 502, 503, 504].includes(status) && attempt < maxRetries - 1) {
                const retryAfter = e.headers?.["retry-after"];
                const delay = retryAfter 
                    ? parseInt(retryAfter) * 1000
                    : (2 ** attempt + Math.random()) * 1000;
                await new Promise(r => setTimeout(r, delay));
            }
            // 500: retry cautiously
            else if (status === 500 && attempt < 3) {
                const delay = (2 ** attempt + Math.random()) * 1000;
                await new Promise(r => setTimeout(r, delay));
            } else {
                throw e;
            }
        }
    }
}
```

#### cURL

```bash
#!/bin/bash

MAX_RETRIES=5
ATTEMPT=0

while [ $ATTEMPT -lt $MAX_RETRIES ]; do
  RESPONSE=$(curl -w "\n%{http_code}" -s -X POST https://api.speechify.ai/v1/audio/speech \
    -H "Authorization: Bearer $SPEECHIFY_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "input": "Hello world",
      "voice_id": "geffen_32",
      "model": "simba-3.2",
      "audio_format": "mp3"
    }')
  
  HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
  BODY=$(echo "$RESPONSE" | sed '$d')
  
  # Success
  if [ "$HTTP_CODE" -eq 200 ]; then
    echo "$BODY"
    exit 0
  fi
  
  # Transient errors: retry with backoff
  if [ "$HTTP_CODE" -eq 429 ] || [ "$HTTP_CODE" -eq 502 ] || \
     [ "$HTTP_CODE" -eq 503 ] || [ "$HTTP_CODE" -eq 504 ]; then
    if [ $ATTEMPT -lt $((MAX_RETRIES - 1)) ]; then
      DELAY=$((2 ** ATTEMPT))
      echo "Retrying in ${DELAY}s..." >&2
      sleep $DELAY
      ATTEMPT=$((ATTEMPT + 1))
      continue
    fi
  fi
  
  # 500: retry cautiously (max 3 attempts)
  if [ "$HTTP_CODE" -eq 500 ] && [ $ATTEMPT -lt 3 ]; then
    DELAY=$((2 ** ATTEMPT))
    echo "Server error, retrying in ${DELAY}s..." >&2
    sleep $DELAY
    ATTEMPT=$((ATTEMPT + 1))
    continue
  fi
  
  # Non-retryable error
  echo "Error $HTTP_CODE: $BODY" >&2
  exit 1
done

echo "Max retries exceeded" >&2
exit 1
```

### Retry-After header

When present, `Retry-After` tells you how long to wait (in seconds) before retrying. Respect this value to avoid hammering the API.

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 5
```

Wait at least 5 seconds before the next attempt.

## Logging and debugging

Always log the `Speechify-Request-Id` from error responses. Include it when contacting support:

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 10 seconds."
  },
  "request_id": "req_7f3a2c1b4d5e6f7a"
}
```

The `request_id` uniquely identifies the failed request in Speechify's logs.

## FAQ

#### Should I retry 4xx errors?

No. A `4xx` (except `429`) means the request itself is wrong. Retrying it unchanged will fail again. Fix the input, auth, or resource ID, then try a new request.

#### How many times should I retry?

For transient errors (`429`, `502`, `503`, `504`), retry 3-5 times with exponential backoff. For `500`, retry cautiously (2-3 times max) and stop if it persists.

#### What if an error occurs mid-stream?

HTTP chunked responses (e.g., `/v1/audio/stream`) cannot send error messages after the stream starts. If the connection closes early, check total bytes received and retry the remaining text. See [Streaming](https://docs.speechify.ai/build/guides/text-to-speech/streaming#error-handling).