Error Handling¶
Best practices for handling errors when using the SubX API.
HTTP Status Codes¶
| Code | Meaning | Action |
|---|---|---|
| 200 | Success | Process the response |
| 400 | Bad Request | Check your request parameters |
| 401 | Unauthorized | Verify your API key |
| 404 | Not Found | Resource doesn't exist |
| 429 | Rate Limited | Wait for Retry-After seconds, then retry |
| 500 | Server Error | Retry with backoff |
| 502 | Bad Gateway | Source unavailable, retry later |
Error Response Format¶
All error responses follow this format:
For validation errors (400), the response may include detailed field information:
{
"detail": [
{
"loc": ["query", "year"],
"msg": "ensure this value is greater than or equal to 1900",
"type": "value_error"
}
]
}
Python Error Handling¶
import requests
import time
def search_with_error_handling(api_key, params, max_retries=3):
"""Search with comprehensive error handling."""
headers = {"Authorization": f"Bearer {api_key}"}
url = "https://subx-api.duckdns.org/api/subtitles/search"
for attempt in range(max_retries):
try:
response = requests.get(url, headers=headers, params=params, timeout=10)
# Handle specific status codes
if response.status_code == 200:
return response.json()
elif response.status_code == 400:
print(f"Bad Request: {response.json()['detail']}")
return None # Don't retry
elif response.status_code == 401:
print("Unauthorized: Check your API key")
return None # Don't retry
elif response.status_code == 404:
print("Not Found: Resource doesn't exist")
return None # Don't retry
elif response.status_code == 429:
# Rate limited - use Retry-After header
retry_after = int(response.headers.get("Retry-After", 60 * (attempt + 1)))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
elif response.status_code >= 500:
# Server error - retry
if attempt < max_retries - 1:
wait_time = 60 * (attempt + 1)
print(f"Server error. Retrying in {wait_time}s...")
time.sleep(wait_time)
continue
else:
print("Server error persists after retries")
return None
except requests.exceptions.Timeout:
print(f"Request timed out (attempt {attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
time.sleep(60 * (attempt + 1))
continue
except requests.exceptions.ConnectionError:
print(f"Connection error (attempt {attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
time.sleep(60 * (attempt + 1))
continue
except Exception as e:
print(f"Unexpected error: {e}")
return None
return None
Validation Before Requests¶
Validate parameters before making requests:
def validate_search_params(params):
"""Validate search parameters before making request."""
errors = []
# At least one search criteria required
if not any([params.get(k) for k in ['query', 'title', 'imdb_id', 'public_id']]):
errors.append("At least one search criteria required")
# Year validation
if 'year' in params:
year = params['year']
if not (1900 <= year <= 2100):
errors.append(f"Year must be between 1900 and 2100, got {year}")
# Limit validation
if 'limit' in params:
limit = params['limit']
if not (1 <= limit <= 200):
errors.append(f"Limit must be between 1 and 200, got {limit}")
if errors:
raise ValueError(f"Invalid parameters: {', '.join(errors)}")
return True
Timeout Configuration¶
Always set timeouts to prevent hanging requests:
# Short timeout for health checks
response = requests.get("https://subx-api.duckdns.org/api/health", timeout=5)
# Standard timeout for API requests
response = requests.get("https://subx-api.duckdns.org/api/subtitles/search",
headers=headers, params=params, timeout=10)
# Longer timeout for downloads
response = requests.get(f"https://subx-api.duckdns.org/api/subtitles/{id}/download",
headers=headers, timeout=60)
Next Steps¶
- Rate Limits - Understanding rate limits