Error Codes
Understand API error responses and handle them gracefully in your application.
When an API request fails, Lesan AI returns a JSON error response with a consistent format. This page covers all error types, their codes, and how to handle them programmatically.
Error Response Format
All error responses follow this structure:
{
"error": {
"type": "invalid_request_error",
"code": "missing_required_field",
"message": "The 'language' field is required.",
"param": "language"
}
}- type — The category of error (see error types below)
- code — A machine-readable error code for programmatic handling
- message — A human-readable description of what went wrong
- param — (optional) The request parameter that caused the error
HTTP Status Codes
400 — Bad Request
The request was malformed or missing required parameters.
// Error type: invalid_request_error
// Common codes:
// missing_required_field — A required field was not provided
// invalid_field_value — A field value is not valid (e.g., unsupported language)
// invalid_audio_format — The audio file format is not supported
// file_too_large — The audio file exceeds the maximum size limit
// invalid_json — The request body is not valid JSON
{
"error": {
"type": "invalid_request_error",
"code": "invalid_field_value",
"message": "Unsupported language 'xx'. Supported: am, ti, so, en.",
"param": "language"
}
}401 — Unauthorized
Authentication failed or no API key was provided.
// Error type: authentication_error
// Common codes:
// missing_api_key — No Authorization header provided
// invalid_api_key — The API key is malformed or does not exist
// expired_api_key — The API key has expired
// revoked_api_key — The API key has been revoked
{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "The API key provided is invalid."
}
}403 — Forbidden
The API key does not have permission for the requested operation.
// Error type: permission_error
// Common codes:
// insufficient_scope — The key lacks the required scope
// ip_not_allowed — Request IP is not in the key's allowlist
// origin_not_allowed — Request origin is not in the key's allowlist
{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "This API key does not have the 'write' scope."
}
}404 — Not Found
The requested resource does not exist.
// Error type: not_found_error
// Common codes:
// job_not_found — The transcription job ID does not exist
// webhook_not_found — The webhook ID does not exist
// resource_not_found — Generic resource not found
{
"error": {
"type": "not_found_error",
"code": "job_not_found",
"message": "Job 'job_abc123' was not found."
}
}429 — Too Many Requests
You have exceeded the rate limit. See the Rate Limits guide for details.
// Error type: rate_limit_error
// Common codes:
// rate_limit_exceeded — Too many requests per minute
// concurrent_limit_exceeded — Too many concurrent jobs
// daily_quota_exceeded — Daily usage quota reached
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 2 seconds.",
"retry_after": 2
}
}When you receive a 429 response, check the retry_after field for the number of seconds to wait before retrying.
500 — Internal Server Error
An unexpected error occurred on the server. These are rare and typically resolve on their own.
// Error type: server_error
// Common codes:
// internal_error — An unexpected internal error occurred
// service_unavailable — The service is temporarily unavailable
// model_error — The ML model encountered an error
{
"error": {
"type": "server_error",
"code": "internal_error",
"message": "An internal error occurred. Please try again."
}
}Handling Errors
Always check the HTTP status code and parse the error response body to handle errors gracefully:
import requests
import time
def transcribe_with_retry(audio_url, language, max_retries=3):
url = "https://asr.lesan.ai/transcribe"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"audio_url": audio_url,
"language": language,
"mode": "sync"
}
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
return response.json()
error = response.json().get("error", {})
error_type = error.get("type")
error_code = error.get("code")
# Don't retry client errors (except rate limits)
if response.status_code == 400:
raise ValueError(f"Bad request: {error.get('message')}")
elif response.status_code == 401:
raise PermissionError(f"Auth failed: {error.get('message')}")
elif response.status_code == 429:
retry_after = error.get("retry_after", 2 ** attempt)
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
elif response.status_code >= 500:
wait = 2 ** attempt
print(f"Server error. Retrying in {wait}s...")
time.sleep(wait)
continue
else:
raise Exception(f"Unexpected error: {response.status_code}")
raise Exception("Max retries exceeded")Retry Strategy
Follow these guidelines when implementing retry logic:
- 429 errors — Always retry. Use the
retry_aftervalue if provided, otherwise use exponential backoff. - 500 errors — Retry with exponential backoff (1s, 2s, 4s). Stop after 3 attempts.
- 400 errors — Do not retry. Fix the request parameters.
- 401 errors — Do not retry. Check your API key.
- 403 errors — Do not retry. Check your API key permissions.
See the Rate Limits guide for quota details, or the Best Practices guide for production error handling patterns.