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
403 ForbiddenAuthenticated but not authorizedConfirm workspace and permissions
404 Not FoundResource does not existVerify resource ID
409 ConflictIdempotency key reused with different bodyUse a fresh key or match the original body

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:

1import time
2import random
3from speechify import Speechify
4
5client = Speechify()
6
7def generate_with_retry(text, max_retries=5):
8 for attempt in range(max_retries):
9 try:
10 return client.audio.speech(
11 input=text,
12 voice_id="geffen_32",
13 model="simba-3.2",
14 audio_format="mp3",
15 )
16 except Exception as e:
17 status = getattr(e, 'status_code', None)
18
19 # Transient errors: retry with backoff
20 if status in [429, 502, 503, 504] and attempt < max_retries - 1:
21 # Check for Retry-After header
22 retry_after = getattr(e, 'headers', {}).get('Retry-After')
23 if retry_after:
24 time.sleep(int(retry_after))
25 else:
26 # Exponential backoff with jitter
27 delay = (2 ** attempt) + random.uniform(0, 1)
28 time.sleep(delay)
29 # 500: retry cautiously
30 elif status == 500 and attempt < 3:
31 delay = (2 ** attempt) + random.uniform(0, 1)
32 time.sleep(delay)
33 else:
34 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.

1HTTP/1.1 429 Too Many Requests
2Retry-After: 5

Wait at least 5 seconds before the next attempt.

Idempotency on retries

Retrying a mutating POST (anything that creates a call, batch, purchase, or resource) risks performing the action twice if the first attempt succeeded but the response was lost.

Only endpoints that explicitly document Idempotency-Key support will replay the original response. Check the endpoint reference before relying on idempotency protection.

For supported endpoints, send an Idempotency-Key header with a unique value per logical operation. Reuse the same key on every retry:

$curl -X POST https://api.speechify.ai/v1/agents/outbound-calls \
> -H "Authorization: Bearer $SPEECHIFY_API_KEY" \
> -H "Idempotency-Key: 4c7f0a78-0a61-42b4-8b8a-60b307b18f0c" \
> -H "Content-Type: application/json" \
> -d '{"agent_id": "agent_demo0001", "to": "+15555550123"}'

The server runs the request once and replays the original response on retries. Endpoints that do not document idempotency support will ignore the header. See Idempotency for details.

Logging and debugging

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

1{
2 "error": {
3 "code": "rate_limited",
4 "message": "Rate limit exceeded. Retry after 10 seconds."
5 },
6 "request_id": "req_7f3a2c1b4d5e6f7a"
7}

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.