# API Documentation ## Document Purpose This document provides comprehensive REST API and WebSocket reference for the Aniworld application. Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L1-L252) --- ## 1. API Overview ### Base URL and Versioning | Environment | Base URL | | ------------------- | --------------------------------- | | Local Development | `http://127.0.0.1:8000` | | API Documentation | `http://127.0.0.1:8000/api/docs` | | ReDoc Documentation | `http://127.0.0.1:8000/api/redoc` | The API does not use versioning prefixes. All endpoints are available under `/api/*`. Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L177-L184) ### Authentication The API uses JWT Bearer Token authentication. **Header Format:** ``` Authorization: Bearer ``` **Public Endpoints (no authentication required):** - `/api/auth/*` - Authentication endpoints - `/api/health` - Health check endpoints - `/api/docs`, `/api/redoc` - API documentation - `/static/*` - Static files - `/`, `/login`, `/setup`, `/queue` - UI pages Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L39-L52) ### Content Types | Direction | Content-Type | | --------- | ----------------------------- | | Request | `application/json` | | Response | `application/json` | | WebSocket | `application/json` (messages) | ### Common Headers | Header | Required | Description | | --------------- | -------- | ------------------------------------ | | `Authorization` | Yes\* | Bearer token for protected endpoints | | `Content-Type` | Yes | `application/json` for POST/PUT | | `Origin` | No | Required for CORS preflight | \*Not required for public endpoints listed above. --- ## 2. Authentication Endpoints Prefix: `/api/auth` Source: [src/server/api/auth.py](../src/server/api/auth.py#L1-L180) ### POST /api/auth/setup Initial setup endpoint to configure the master password. Can only be called once. **Request Body:** ```json { "master_password": "string (min 8 chars, mixed case, number, special char)", "anime_directory": "string (optional, path to anime folder)" } ``` **Response (201 Created):** ```json { "status": "ok" } ``` **Errors:** - `400 Bad Request` - Master password already configured or invalid password Source: [src/server/api/auth.py](../src/server/api/auth.py#L28-L90) ### POST /api/auth/login Validate master password and return JWT token. **Request Body:** ```json { "password": "string", "remember": false } ``` **Response (200 OK):** ```json { "access_token": "eyJ...", "token_type": "bearer", "expires_at": "2025-12-14T10:30:00Z" } ``` **Errors:** - `401 Unauthorized` - Invalid credentials - `429 Too Many Requests` - Account locked due to failed attempts Source: [src/server/api/auth.py](../src/server/api/auth.py#L93-L124) ### POST /api/auth/logout Logout by revoking token. **Response (200 OK):** ```json { "status": "ok", "message": "Logged out successfully" } ``` Source: [src/server/api/auth.py](../src/server/api/auth.py#L127-L140) ### GET /api/auth/status Return whether master password is configured and if caller is authenticated. **Response (200 OK):** ```json { "configured": true, "authenticated": true } ``` Source: [src/server/api/auth.py](../src/server/api/auth.py#L157-L162) --- ## 3. Anime Endpoints Prefix: `/api/anime` Source: [src/server/api/anime.py](../src/server/api/anime.py#L1-L812) ### Series Identifier Convention The API uses two identifier fields: | Field | Purpose | Example | | -------- | ---------------------------------------------------- | -------------------------- | | `key` | **Primary identifier** - provider-assigned, URL-safe | `"attack-on-titan"` | | `folder` | Metadata only - filesystem folder name | `"Attack on Titan (2013)"` | Use `key` for all API operations. The `folder` field is for display purposes only. ### GET /api/anime/status Get anime library status information. **Authentication:** Required **Response (200 OK):** ```json { "directory": "/path/to/anime", "series_count": 42 } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L28-L58) ### GET /api/anime List library series that have missing episodes. **Authentication:** Required **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `page` | int | 1 | Page number (must be positive) | | `per_page` | int | 20 | Items per page (max 1000) | | `sort_by` | string | null | Sort field: `title`, `id`, `name`, `missing_episodes` | | `filter` | string | null | Filter term | **Response (200 OK):** ```json [ { "key": "beheneko-the-elf-girls-cat", "name": "Beheneko", "site": "aniworld.to", "folder": "beheneko the elf girls cat (2025)", "missing_episodes": { "1": [1, 2, 3, 4] }, "link": "" } ] ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L155-L303) ### GET /api/anime/search Search the provider for anime series matching a query. **Authentication:** Not required **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `query` | string | Yes | Search term (max 200 chars) | **Response (200 OK):** ```json [ { "key": "attack-on-titan", "name": "Attack on Titan", "site": "aniworld.to", "folder": "Attack on Titan (2013)", "missing_episodes": {}, "link": "https://aniworld.to/anime/stream/attack-on-titan" } ] ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L431-L474) ### POST /api/anime/search Search via POST body. **Request Body:** ```json { "query": "attack on titan" } ``` **Response:** Same as GET /api/anime/search Source: [src/server/api/anime.py](../src/server/api/anime.py#L477-L495) ### POST /api/anime/add Add a new series to the library. **Authentication:** Required **Request Body:** ```json { "link": "https://aniworld.to/anime/stream/attack-on-titan", "name": "Attack on Titan" } ``` **Response (200 OK):** ```json { "status": "success", "message": "Successfully added series: Attack on Titan", "key": "attack-on-titan", "folder": "Attack on Titan", "db_id": 1 } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L604-L710) ### POST /api/anime/rescan Trigger a rescan of the local library. **Authentication:** Required **Response (200 OK):** ```json { "success": true, "message": "Rescan started successfully" } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L306-L337) ### GET /api/anime/{anime_id} Return detailed information about a specific series. **Authentication:** Not required **Path Parameters:** | Parameter | Description | |-----------|-------------| | `anime_id` | Series `key` (primary) or `folder` (deprecated fallback) | **Response (200 OK):** ```json { "key": "attack-on-titan", "title": "Attack on Titan", "folder": "Attack on Titan (2013)", "episodes": ["1-1", "1-2", "1-3"], "description": null } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L713-L793) --- ## 4. Download Queue Endpoints Prefix: `/api/queue` Source: [src/server/api/download.py](../src/server/api/download.py#L1-L529) ### GET /api/queue/status Get current download queue status and statistics. **Authentication:** Required **Response (200 OK):** ```json { "status": { "is_running": false, "is_paused": false, "active_downloads": [], "pending_queue": [], "completed_downloads": [], "failed_downloads": [] }, "statistics": { "total_items": 5, "pending_count": 3, "active_count": 1, "completed_count": 1, "failed_count": 0, "total_downloaded_mb": 1024.5, "average_speed_mbps": 2.5, "estimated_time_remaining": 3600 } } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L21-L56) ### POST /api/queue/add Add episodes to the download queue. **Authentication:** Required **Request Body:** ```json { "serie_id": "attack-on-titan", "serie_folder": "Attack on Titan (2013)", "serie_name": "Attack on Titan", "episodes": [ { "season": 1, "episode": 1, "title": "Episode 1" }, { "season": 1, "episode": 2, "title": "Episode 2" } ], "priority": "NORMAL" } ``` **Priority Values:** `LOW`, `NORMAL`, `HIGH` **Response (201 Created):** ```json { "status": "success", "message": "Added 2 episode(s) to download queue", "added_items": ["uuid1", "uuid2"], "item_ids": ["uuid1", "uuid2"], "failed_items": [] } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L59-L120) ### POST /api/queue/start Start automatic queue processing. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing started" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L293-L331) ### POST /api/queue/stop Stop processing new downloads from queue. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing stopped (current download will continue)" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L334-L387) ### POST /api/queue/pause Pause queue processing (alias for stop). **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing paused" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L416-L445) ### DELETE /api/queue/{item_id} Remove a specific item from the download queue. **Authentication:** Required **Path Parameters:** | Parameter | Description | |-----------|-------------| | `item_id` | Download item UUID | **Response (204 No Content)** Source: [src/server/api/download.py](../src/server/api/download.py#L225-L256) ### DELETE /api/queue Remove multiple items from the download queue. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid1", "uuid2"] } ``` **Response (204 No Content)** Source: [src/server/api/download.py](../src/server/api/download.py#L259-L290) ### DELETE /api/queue/completed Clear completed downloads from history. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Cleared 5 completed item(s)", "count": 5 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L123-L149) ### DELETE /api/queue/failed Clear failed downloads from history. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Cleared 2 failed item(s)", "count": 2 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L152-L178) ### DELETE /api/queue/pending Clear all pending downloads from the queue. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Removed 10 pending item(s)", "count": 10 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L181-L207) ### POST /api/queue/reorder Reorder items in the pending queue. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid3", "uuid1", "uuid2"] } ``` **Response (200 OK):** ```json { "status": "success", "message": "Queue reordered with 3 items" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L448-L477) ### POST /api/queue/retry Retry failed downloads. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid1", "uuid2"] } ``` Pass empty `item_ids` array to retry all failed items. **Response (200 OK):** ```json { "status": "success", "message": "Retrying 2 failed item(s)", "retried_count": 2, "retried_ids": ["uuid1", "uuid2"] } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L480-L514) --- ## 5. Configuration Endpoints Prefix: `/api/config` Source: [src/server/api/config.py](../src/server/api/config.py#L1-L374) ### GET /api/config Return current application configuration. **Authentication:** Required **Response (200 OK):** ```json { "name": "Aniworld", "data_dir": "data", "scheduler": { "enabled": true, "interval_minutes": 60 }, "logging": { "level": "INFO", "file": null, "max_bytes": null, "backup_count": 3 }, "backup": { "enabled": false, "path": "data/backups", "keep_days": 30 }, "other": {} } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L16-L27) ### PUT /api/config Apply an update to the configuration. **Authentication:** Required **Request Body:** ```json { "scheduler": { "enabled": true, "interval_minutes": 30 }, "logging": { "level": "DEBUG" } } ``` **Response (200 OK):** Updated configuration object Source: [src/server/api/config.py](../src/server/api/config.py#L30-L47) ### POST /api/config/validate Validate a configuration without applying it. **Authentication:** Required **Request Body:** Full `AppConfig` object **Response (200 OK):** ```json { "valid": true, "errors": [] } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L50-L64) ### GET /api/config/backups List all available configuration backups. **Authentication:** Required **Response (200 OK):** ```json [ { "name": "config_backup_20251213_090130.json", "size": 1024, "created": "2025-12-13T09:01:30Z" } ] ``` Source: [src/server/api/config.py](../src/server/api/config.py#L67-L81) ### POST /api/config/backups Create a backup of the current configuration. **Authentication:** Required **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | No | Custom backup name | **Response (200 OK):** ```json { "name": "config_backup_20251213_090130.json", "message": "Backup created successfully" } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L84-L102) ### POST /api/config/backups/{backup_name}/restore Restore configuration from a backup. **Authentication:** Required **Response (200 OK):** Restored configuration object Source: [src/server/api/config.py](../src/server/api/config.py#L105-L123) ### DELETE /api/config/backups/{backup_name} Delete a configuration backup. **Authentication:** Required **Response (200 OK):** ```json { "message": "Backup 'config_backup_20251213.json' deleted successfully" } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L126-L142) ### POST /api/config/directory Update anime directory configuration. **Authentication:** Required **Request Body:** ```json { "directory": "/path/to/anime" } ``` **Response (200 OK):** ```json { "message": "Anime directory updated successfully", "synced_series": 15 } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L189-L247) --- ## 6. Scheduler Endpoints Prefix: `/api/scheduler` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L1-L122) ### GET /api/scheduler/config Get current scheduler configuration. **Authentication:** Required **Response (200 OK):** ```json { "enabled": true, "interval_minutes": 60 } ``` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L22-L42) ### POST /api/scheduler/config Update scheduler configuration. **Authentication:** Required **Request Body:** ```json { "enabled": true, "interval_minutes": 30 } ``` **Response (200 OK):** Updated scheduler configuration Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L45-L75) ### POST /api/scheduler/trigger-rescan Manually trigger a library rescan. **Authentication:** Required **Response (200 OK):** ```json { "success": true, "message": "Rescan started successfully" } ``` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L78-L122) --- ## 7. Health Check Endpoints Prefix: `/health` Source: [src/server/api/health.py](../src/server/api/health.py#L1-L267) ### GET /health Basic health check endpoint. **Authentication:** Not required **Response (200 OK):** ```json { "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "version": "1.0.0" } ``` Source: [src/server/api/health.py](../src/server/api/health.py#L151-L161) ### GET /health/detailed Comprehensive health check with database, filesystem, and system metrics. **Authentication:** Not required **Response (200 OK):** ```json { "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "version": "1.0.0", "dependencies": { "database": { "status": "healthy", "connection_time_ms": 1.5, "message": "Database connection successful" }, "filesystem": { "status": "healthy", "data_dir_writable": true, "logs_dir_writable": true }, "system": { "cpu_percent": 25.0, "memory_percent": 45.0, "memory_available_mb": 8192.0, "disk_percent": 60.0, "disk_free_mb": 102400.0, "uptime_seconds": 86400.0 } }, "startup_time": "2025-12-13T08:00:00.000Z" } ``` Source: [src/server/api/health.py](../src/server/api/health.py#L164-L200) --- ## 8. WebSocket Protocol Endpoint: `/ws/connect` Source: [src/server/api/websocket.py](../src/server/api/websocket.py#L1-L260) ### Connection **URL:** `ws://127.0.0.1:8000/ws/connect` **Query Parameters:** | Parameter | Required | Description | |-----------|----------|-------------| | `token` | No | JWT token for authenticated access | ### Message Types | Type | Direction | Description | | ------------------- | ---------------- | -------------------------- | | `connected` | Server -> Client | Connection confirmation | | `ping` | Client -> Server | Keepalive request | | `pong` | Server -> Client | Keepalive response | | `download_progress` | Server -> Client | Download progress update | | `download_complete` | Server -> Client | Download completed | | `download_failed` | Server -> Client | Download failed | | `download_added` | Server -> Client | Item added to queue | | `download_removed` | Server -> Client | Item removed from queue | | `queue_status` | Server -> Client | Queue status update | | `queue_started` | Server -> Client | Queue processing started | | `queue_stopped` | Server -> Client | Queue processing stopped | | `scan_progress` | Server -> Client | Library scan progress | | `scan_complete` | Server -> Client | Library scan completed | | `system_info` | Server -> Client | System information message | | `error` | Server -> Client | Error message | Source: [src/server/models/websocket.py](../src/server/models/websocket.py#L25-L57) ### Room Subscriptions Clients can join/leave rooms to receive specific updates. **Join Room:** ```json { "action": "join", "data": { "room": "downloads" } } ``` **Leave Room:** ```json { "action": "leave", "data": { "room": "downloads" } } ``` **Available Rooms:** - `downloads` - Download progress and status updates ### Server Message Format ```json { "type": "download_progress", "timestamp": "2025-12-13T10:30:00.000Z", "data": { "download_id": "uuid-here", "key": "attack-on-titan", "folder": "Attack on Titan (2013)", "percent": 45.2, "speed_mbps": 2.5, "eta_seconds": 180 } } ``` ### WebSocket Status Endpoint **GET /ws/status** Returns WebSocket service status. **Response (200 OK):** ```json { "status": "operational", "active_connections": 5, "supported_message_types": [ "download_progress", "download_complete", "download_failed", "queue_status", "connected", "ping", "pong", "error" ] } ``` Source: [src/server/api/websocket.py](../src/server/api/websocket.py#L238-L260) --- ## 9. Data Models ### Download Item ```json { "id": "uuid-string", "serie_id": "attack-on-titan", "serie_folder": "Attack on Titan (2013)", "serie_name": "Attack on Titan", "episode": { "season": 1, "episode": 1, "title": "To You, in 2000 Years" }, "status": "pending", "priority": "NORMAL", "added_at": "2025-12-13T10:00:00Z", "started_at": null, "completed_at": null, "progress": null, "error": null, "retry_count": 0, "source_url": null } ``` **Status Values:** `pending`, `downloading`, `paused`, `completed`, `failed`, `cancelled` **Priority Values:** `LOW`, `NORMAL`, `HIGH` Source: [src/server/models/download.py](../src/server/models/download.py#L63-L118) ### Episode Identifier ```json { "season": 1, "episode": 1, "title": "Episode Title" } ``` Source: [src/server/models/download.py](../src/server/models/download.py#L36-L41) ### Download Progress ```json { "percent": 45.2, "downloaded_mb": 256.0, "total_mb": 512.0, "speed_mbps": 2.5, "eta_seconds": 180 } ``` Source: [src/server/models/download.py](../src/server/models/download.py#L44-L60) --- ## 10. Error Handling ### HTTP Status Codes | Code | Meaning | When Used | | ---- | --------------------- | --------------------------------- | | 200 | OK | Successful request | | 201 | Created | Resource created | | 204 | No Content | Successful deletion | | 400 | Bad Request | Invalid request body/parameters | | 401 | Unauthorized | Missing or invalid authentication | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource does not exist | | 422 | Unprocessable Entity | Validation error | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Server-side error | ### Error Response Format ```json { "success": false, "error": "VALIDATION_ERROR", "message": "Human-readable error message", "details": { "field": "Additional context" }, "request_id": "uuid-for-tracking" } ``` Source: [src/server/middleware/error_handler.py](../src/server/middleware/error_handler.py#L26-L56) ### Common Error Codes | Error Code | HTTP Status | Description | | ---------------------- | ----------- | ------------------------------ | | `AUTHENTICATION_ERROR` | 401 | Invalid or missing credentials | | `AUTHORIZATION_ERROR` | 403 | Insufficient permissions | | `VALIDATION_ERROR` | 422 | Request validation failed | | `NOT_FOUND_ERROR` | 404 | Resource not found | | `CONFLICT_ERROR` | 409 | Resource conflict | | `RATE_LIMIT_ERROR` | 429 | Rate limit exceeded | --- ## 11. Rate Limiting ### Authentication Endpoints | Endpoint | Limit | Window | | ---------------------- | ---------- | ---------- | | `POST /api/auth/login` | 5 requests | 60 seconds | | `POST /api/auth/setup` | 5 requests | 60 seconds | Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L143-L162) ### Origin-Based Limiting All endpoints from the same origin are limited to 60 requests per minute per origin. Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L115-L133) ### Rate Limit Response ```json { "detail": "Too many authentication attempts, try again later" } ``` HTTP Status: 429 Too Many Requests --- ## 12. Pagination The anime list endpoint supports pagination. **Query Parameters:** | Parameter | Default | Max | Description | |-----------|---------|-----|-------------| | `page` | 1 | - | Page number (1-indexed) | | `per_page` | 20 | 1000 | Items per page | **Example:** ``` GET /api/anime?page=2&per_page=50 ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L180-L220)