Error Handling
Understand error responses and how to handle them gracefully.
Error Response Format
All API errors follow a consistent JSON structure for easy parsing:
json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"category": "error_category",
"resolution": "Suggested steps to fix the issue"
},
"request_id": "req_abc123xyz",
"timestamp": 1699820400.123
}
| Field | Description |
|---|---|
success | Always false for error responses |
error.code | Machine-readable error code for programmatic handling |
error.message | Human-readable description of the error |
error.category | Error category (authentication, validation, rate_limit, etc.) |
error.resolution | Suggested steps to resolve the issue |
request_id | Unique ID for tracking this request (useful for support) |
timestamp | Unix timestamp when error occurred |
HTTP Status Codes
The API uses standard HTTP status codes to indicate request outcome:
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request successful |
| 400 | Bad Request | Invalid parameters or malformed request |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Valid key but insufficient permissions |
| 404 | Not Found | Resource or endpoint not found |
| 422 | Unprocessable | Valid request but failed validation |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Internal server error |
| 503 | Service Unavailable | Service temporarily unavailable |
Error Categories
Errors are grouped into categories for easier handling:
Authentication Errors
| Code | Message | Resolution |
|---|---|---|
INVALID_API_KEY |
The API key provided is invalid | Check your API key is correct and active |
MISSING_API_KEY |
No API key provided | Include X-API-Key header in your request |
API_KEY_EXPIRED |
Your API key has expired | Generate a new API key from your dashboard |
API_KEY_REVOKED |
This API key has been revoked | Contact support or generate a new key |
Validation Errors
| Code | Message | Resolution |
|---|---|---|
INVALID_TEXT |
Text parameter is invalid or empty | Provide non-empty text (1-10,000 characters) |
TEXT_TOO_LONG |
Text exceeds maximum length | Split text into smaller chunks (max 10,000 chars) |
INVALID_VOICE |
The specified voice does not exist | Use /voices endpoint to get valid voice IDs |
INVALID_MODEL |
The specified model is invalid | Use /models endpoint to get valid model names |
INVALID_FORMAT |
Unsupported output format | Use mp3 or wav |
INVALID_SPEED |
Speed value out of range | Use a value between 0.5 and 2.0 |
Rate Limit Errors
| Code | Message | Resolution |
|---|---|---|
RATE_LIMIT_EXCEEDED |
Too many requests | Wait and retry with exponential backoff |
QUOTA_EXCEEDED |
Monthly character quota exhausted | Upgrade your plan or wait for quota reset |
DAILY_LIMIT_REACHED |
Daily request limit reached | Wait until tomorrow or upgrade your plan |
Permission Errors
| Code | Message | Resolution |
|---|---|---|
MODEL_ACCESS_DENIED |
Your plan doesn't include this model | Upgrade to a plan with access to this model |
VOICE_ACCESS_DENIED |
Premium voice not available on your plan | Upgrade to access premium voices |
FEATURE_DISABLED |
This feature is not enabled for your account | Contact support to enable this feature |
Handling Errors
Best practices for robust error handling:
python
import requests
import time
def generate_tts_with_retry(text, voice_id, max_retries=3):
"""Generate TTS with automatic retry on transient errors."""
url = "https://yourvoic.com/api/v1/tts/generate"
headers = {"X-API-Key": "your_api_key"}
data = {"text": text, "voice_id": voice_id}
for attempt in range(max_retries):
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
return response.content # Success
error = response.json().get("error", {})
error_code = error.get("code")
# Don't retry client errors (except rate limits)
if response.status_code == 400:
raise ValueError(f"Invalid request: {error.get('message')}")
if response.status_code == 401:
raise AuthError(f"Authentication failed: {error.get('message')}")
if response.status_code == 403:
raise PermissionError(f"Access denied: {error.get('message')}")
if response.status_code == 429:
# Rate limited - wait and retry
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
if response.status_code >= 500:
# Server error - exponential backoff
wait_time = (2 ** attempt) + random.random()
print(f"Server error. Retrying in {wait_time:.1f}s...")
time.sleep(wait_time)
continue
raise Exception(f"Max retries exceeded. Last error: {error_code}")
javascript
async function generateTTSWithRetry(text, voiceId, maxRetries = 3) {
const url = 'https://yourvoic.com/api/v1/tts/generate';
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your_api_key'
},
body: JSON.stringify({ text, voice_id: voiceId })
});
if (response.ok) {
return await response.blob();
}
const error = await response.json();
const errorCode = error.error?.code;
// Handle specific errors
switch (response.status) {
case 400:
throw new Error(`Invalid request: ${error.error?.message}`);
case 401:
throw new Error(`Auth failed: ${error.error?.message}`);
case 403:
throw new Error(`Access denied: ${error.error?.message}`);
case 429:
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await sleep(retryAfter * 1000);
continue;
default:
if (response.status >= 500) {
const waitTime = Math.pow(2, attempt) * 1000;
console.log(`Server error. Retrying in ${waitTime}ms...`);
await sleep(waitTime);
continue;
}
}
} catch (e) {
if (attempt === maxRetries - 1) throw e;
}
}
throw new Error('Max retries exceeded');
}
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
Retry Strategy
Different errors require different retry approaches:
| Error Type | Should Retry? | Strategy |
|---|---|---|
| 400 Validation | ❌ No | Fix the request parameters |
| 401 Auth | ❌ No | Check/refresh API key |
| 403 Permission | ❌ No | Upgrade plan or contact support |
| 429 Rate Limit | ✅ Yes | Wait for Retry-After header duration |
| 500 Server | ✅ Yes | Exponential backoff (1s, 2s, 4s...) |
| 503 Unavailable | ✅ Yes | Wait and retry with backoff |
💡 Exponential Backoff
For server errors, use exponential backoff: wait 1s, then 2s, then 4s, etc. Add random jitter (0-1s) to prevent thundering herd issues when many clients retry simultaneously.
Debugging Tips
- Save the request_id - Include it when contacting support for faster resolution
- Check response headers - Rate limit headers show remaining quota
- Validate locally - Check text length and parameters before making API calls
- Use the health endpoint - Check
/healthto verify API availability - Log full responses - The resolution field often contains the fix
bash
# Check API health status
curl -s https://yourvoic.com/api/v1/health | jq
# Test your API key
curl -s -H "X-API-Key: your_key" \
https://yourvoic.com/api/v1/usage | jq