Skip to content

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:

{
  "detail": "Error message describing what went wrong"
}

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