Error Handling and Retries

Retry strategy for rate limits and server errors

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.

StatusMeaningAction
429 Too Many RequestsRate or concurrency limit exceededWait for Retry-After header, then retry
502 Bad GatewayUpstream failureRetry with backoff
503 Service UnavailableTemporary overload or maintenanceWait for Retry-After header, then retry
504 Gateway TimeoutUpstream timeoutRetry with backoff

Persistent errors (fix the request)

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

StatusMeaningAction
400 Bad RequestInvalid input, malformed JSON, or unsupported parameterCheck request body and parameters
401 UnauthorizedMissing or invalid API keyVerify Authorization header
402 Payment RequiredInsufficient balance or spend limit reachedTop up balance or raise spend cap
400 content_policy_violationText refused by the content policyEdit the input; retrying unchanged will fail again
403 ForbiddenAuthenticated but not authorizedConfirm workspace and permissions
404 Not FoundResource does not existVerify 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.

Server errors (retry cautiously)

StatusMeaningAction
500 Internal Server ErrorUnexpected server failureRetry 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:

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

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/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:

{
"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

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.

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.

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.