# Suwayomi-Server REST API Documentation This document describes all REST API endpoints available in Suwayomi-Server. **Base URL:** `http://localhost:4567/api/v1/` --- ## Authentication All API endpoints (except where noted) require HTTP Basic Authentication. ### Authorization Header Include the following header in your requests: ``` Authorization: Basic ``` ### How to Generate the Header 1. Combine username and password with a colon: `username:password` 2. Encode the string in Base64 3. Prepend with `Basic ` **Example:** - Username: `admin` - Password: `password123` - Credentials string: `admin:password123` - Base64 encoded: `YWRtaW46cGFzc3dvcmQxMjM=` - Header: `Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=` ### Default Credentials The default username and password are configured in the server settings: - Default username: `admin` - Default password: `admin` **Note:** These should be changed in production via server configuration. ### Authentication Modes The server supports different authentication modes (configured in server settings): 1. **`BASIC_AUTH`** - Requires Basic Auth header for all API requests 2. **`SIMPLE_LOGIN`** - Uses cookie-based session authentication for web interface, but requires Basic Auth for API 3. **`NONE`** - No authentication required (not recommended for production) ### Unauthorized Response If authentication fails, the server returns: **HTTP 401 Unauthorized** ``` WWW-Authenticate: Basic ``` **Response Body:** ```json { "error": "Unauthorized", "message": "Authentication required" } ``` ### WebSocket Authentication WebSocket endpoints use the same Basic Authentication mechanism. Include the Authorization header when establishing the WebSocket connection. --- ## Table of Contents 1. [Extension Endpoints](#extension-endpoints) 2. [Source Endpoints](#source-endpoints) 3. [Manga Endpoints](#manga-endpoints) 4. [Chapter Endpoints](#chapter-endpoints) 5. [Category Endpoints](#category-endpoints) 6. [Backup Endpoints](#backup-endpoints) 7. [Download Endpoints](#download-endpoints) 8. [Update Endpoints](#update-endpoints) 9. [Track Endpoints](#track-endpoints) 10. [Global Meta Endpoints](#global-meta-endpoints) 11. [Settings Endpoints](#settings-endpoints) 12. [WebView Endpoints](#webview-endpoints) 13. [WebSocket Endpoints](#websocket-endpoints) 14. [OPDS Endpoints](#opds-endpoints) --- ## Extension Endpoints ### `GET /api/v1/extension/list` - **What it does:** List all installed extensions - **Expected request:** No parameters required - **Expected response:** JSON array of ExtensionDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "repo": "https://raw.githubusercontent.com/k对身体1/extensions/repo", "apkName": "extensions/kofthub/Dramacafe", "iconUrl": "/extension/icon/Dramacafe.apk", "name": "Dramacafe", "pkgName": "eu.kanade.tachiyomi.extension.en.dramacafe", "versionName": "1.2.3", "versionCode": 12, "lang": "en", "isNsfw": false, "installed": true, "hasUpdate": false, "obsolete": false } ] ``` ### `GET /api/v1/extension/install/{pkgName}` - **What it does:** Install an extension by package name - **Expected request:** Path parameter: `pkgName` (string) - e.g., `eu.kanade.tachiyomi.extension.en.mangadex` - **Expected response:** HTTP 201 (Created), HTTP 302 (Found), or HTTP 500 (Internal Server Error) ### `POST /api/v1/extension/install` - **What it does:** Install an extension from uploaded APK file - **Expected request:** Multipart form data with file field named "file" containing the extension APK - **Expected response:** HTTP 201 (Created), HTTP 302 (Found), or HTTP 500 (Internal Server Error) ### `GET /api/v1/extension/update/{pkgName}` - **What it does:** Update an extension by package name - **Expected request:** Path parameter: `pkgName` (string) - **Expected response:** HTTP 201 (Created), HTTP 302 (Found), HTTP 404 (Not Found), or HTTP 500 (Internal Server Error) ### `GET /api/v1/extension/uninstall/{pkgName}` - **What it does:** Uninstall an extension by package name - **Expected request:** Path parameter: `pkgName` (string) - **Expected response:** HTTP 200 (OK), HTTP 404 (Not Found), or HTTP 500 (Internal Server Error) ### `GET /api/v1/extension/icon/{apkName}` - **What it does:** Get the icon for an extension - **Expected request:** Path parameter: `apkName` (string) - e.g., `Dramacafe.apk` - **Expected response:** Image file (PNG/JPEG) with HTTP 200, or HTTP 404 (Not Found) --- ## Source Endpoints ### `GET /api/v1/source/list` - **What it does:** List all available sources - **Expected request:** No parameters required - **Expected response:** JSON array of SourceDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "id": "1234567890", "name": "MangaDex", "lang": "en", "iconUrl": "/source/icon/1234567890", "supportsLatest": true, "isConfigurable": true, "isNsfw": false, "displayName": "MangaDex", "baseUrl": "https://mangadex.org" } ] ``` ### `GET /api/v1/source/{sourceId}` - **What it does:** Get information about a specific source - **Expected request:** Path parameter: `sourceId` (long) - e.g., `1234567890` - **Expected response:** JSON SourceDataClass object (HTTP 200) or HTTP 404 (Not Found) ### `GET /api/v1/source/{sourceId}/popular/{pageNum}` - **What it does:** Get popular manga from a source - **Expected request:** Path parameters: `sourceId` (long), `pageNum` (int) - **Expected response:** JSON PagedMangaListDataClass object (HTTP 200) **Response JSON Example:** ```json { "mangaList": [ { "id": 1, "sourceId": "1234567890", "url": "/manga/12345", "title": "One Piece", "thumbnailUrl": "https://example.com/cover.jpg", "thumbnailUrlLastFetched": 1699999999, "initialized": true, "artist": "Eiichiro Oda", "author": "Eiichiro Oda", "description": "Monkey D. Luffy sets off on an adventure...", "genre": ["Action", "Adventure", "Comedy"], "status": "ONGOING", "inLibrary": true, "inLibraryAt": 1699999999, "source": null, "meta": {}, "realUrl": "https://mangadex.org/manga/12345", "lastFetchedAt": 1699999999, "chaptersLastFetchedAt": 1699999999, "updateStrategy": "ALWAYS_UPDATE", "freshData": false, "unreadCount": 100, "downloadCount": 0, "chapterCount": 1000, "lastReadAt": null, "lastChapterRead": null, "age": 0, "chaptersAge": 0, "trackers": null } ], "hasNextPage": true } ``` ### `GET /api/v1/source/{sourceId}/latest/{pageNum}` - **What it does:** Get latest manga from a source - **Expected request:** Path parameters: `sourceId` (long), `pageNum` (int) - **Expected response:** JSON PagedMangaListDataClass object (HTTP 200) ### `GET /api/v1/source/{sourceId}/preferences` - **What it does:** Get source preferences/settings - **Expected request:** Path parameter: `sourceId` (long) - **Expected response:** JSON array of preference objects (HTTP 200) **Response JSON Example:** ```json [ { "type": "EditTextPreference", "props": { "key": "username", "title": "Username", "summary": "", "text": "", "default": "" } }, { "type": "SwitchPreferenceCompat", "props": { "key": "hentai", "title": "Show Hentai", "summary": "Show mature content", "checked": false } } ] ``` ### `POST /api/v1/source/{sourceId}/preferences` - **What it does:** Set a source preference - **Expected request:** Path parameter: `sourceId` (long), body: SourcePreferenceChange JSON object **Request JSON Example:** ```json { "position": 0, "value": "myusername" } ``` - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/source/{sourceId}/filters` - **What it does:** Get source filters - **Expected request:** Path parameter: `sourceId` (long), optional query param: `reset` (boolean) - **Expected response:** JSON array of FilterObject objects (HTTP 200) **Response JSON Example:** ```json [ { "type": "Header", "filter": { "name": "Content" } }, { "type": "Select", "filter": { "name": "Genre", "state": 0, "values": ["All", "Action", "Adventure", "Comedy"] } }, { "type": "Text", "filter": { "name": "Year", "state": "" } }, { "type": "CheckBox", "filter": { "name": "Completed", "state": false } } ] ``` ### `POST /api/v1/source/{sourceId}/filters` - **What it does:** Set source filters - **Expected request:** Path parameter: `sourceId` (long), body: FilterChange object or array of FilterChange objects **Request JSON Example (Single filter):** ```json { "position": 1, "state": "2" } ``` **Request JSON Example (Multiple filters):** ```json [ { "position": 1, "state": "2" }, { "position": 2, "state": "2023" }, { "position": 3, "state": "true" } ] ``` - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/source/{sourceId}/search` - **What it does:** Search for manga in a single source - **Expected request:** Path parameter: `sourceId` (long), query params: `searchTerm` (string), `pageNum` (int, default: 1) - **Expected response:** JSON PagedMangaListDataClass object (HTTP 200) ### `POST /api/v1/source/{sourceId}/quick-search` - **What it does:** Quick search manga in a source using filters - **Expected request:** Path parameter: `sourceId` (long), query param: `pageNum` (int, default: 1), body: FilterData object **Request JSON Example:** ```json { "searchTerm": "One Piece", "filter": [ { "position": 1, "state": "0" }, { "position": 2, "state": "" } ] } ``` - **Expected response:** JSON PagedMangaListDataClass object (HTTP 200) --- ## Manga Endpoints ### `GET /api/v1/manga/{mangaId}` - **What it does:** Get manga information - **Expected request:** Path parameter: `mangaId` (int), optional query param: `onlineFetch` (boolean) - **Expected response:** JSON MangaDataClass object (HTTP 200) or HTTP 404 (Not Found) **Response JSON Example:** ```json { "id": 1, "sourceId": "1234567890", "url": "/manga/12345", "title": "One Piece", "thumbnailUrl": "https://example.com/cover.jpg", "thumbnailUrlLastFetched": 1699999999, "initialized": true, "artist": "Eiichiro Oda", "author": "Eiichiro Oda", "description": "Monkey D. Luffy sets off on an adventure...", "genre": ["Action", "Adventure", "Comedy", "Fantasy"], "status": "ONGOING", "inLibrary": true, "inLibraryAt": 1699999999, "source": { "id": "1234567890", "name": "MangaDex", "lang": "en", "iconUrl": "/source/icon/1234567890", "supportsLatest": true, "isConfigurable": true, "isNsfw": false, "displayName": "MangaDex", "baseUrl": "https://mangadex.org" }, "meta": {}, "realUrl": "https://mangadex.org/manga/12345", "lastFetchedAt": 1699999999, "chaptersLastFetchedAt": 1699999999, "updateStrategy": "ALWAYS_UPDATE", "freshData": false, "unreadCount": 100, "downloadCount": 0, "chapterCount": 1000, "lastReadAt": 1699999999, "lastChapterRead": { "id": 100, "url": "/chapter/12345/1", "name": "Chapter 1: Dawn of Adventure", "uploadDate": 1699999999, "chapterNumber": 1.0, "scanlator": "MangaDex", "mangaId": 1, "read": true, "bookmarked": false, "lastPageRead": 20, "lastReadAt": 1699999999, "index": 1, "fetchedAt": 1699999999, "realUrl": "https://mangadex.org/chapter/12345/1", "downloaded": true, "pageCount": 20, "chapterCount": 1000, "meta": {} }, "age": 0, "chaptersAge": 0, "trackers": [ { "trackerId": 1, "trackerName": "MyAnimeList", "remoteId": "12345", "remoteUrl": "https://myanimelist.net/manga/12345", "title": "One Piece", "lastChapterRead": 100, "totalChapters": 1000, "score": 9.5, "status": "Reading", "startedReadingDate": null, "finishedReadingDate": null, "private": false } ] } ``` ### `GET /api/v1/manga/{mangaId}/full` - **What it does:** Get full manga information with all data filled in - **Expected request:** Path parameter: `mangaId` (int), optional query param: `onlineFetch` (boolean) - **Expected response:** JSON MangaDataClass object (HTTP 200) or HTTP 404 (Not Found) ### `GET /api/v1/manga/{mangaId}/thumbnail` - **What it does:** Get manga thumbnail image - **Expected request:** Path parameter: `mangaId` (int) - **Expected response:** Image file (JPEG/PNG) with HTTP 200 or HTTP 404 (Not Found) ### `GET /api/v1/manga/{mangaId}/category` - **What it does:** Get categories assigned to a manga - **Expected request:** Path parameter: `mangaId` (int) - **Expected response:** JSON array of CategoryDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "id": 1, "order": 0, "name": "Favorites", "default": false, "size": 10, "includeInUpdate": "INCLUDE", "includeInDownload": "INCLUDE", "meta": {} } ] ``` ### `GET /api/v1/manga/{mangaId}/category/{categoryId}` - **What it does:** Add manga to a category - **Expected request:** Path parameters: `mangaId` (int), `categoryId` (int) - **Expected response:** HTTP 200 (OK) ### `DELETE /api/v1/manga/{mangaId}/category/{categoryId}` - **What it does:** Remove manga from a category - **Expected request:** Path parameters: `mangaId` (int), `categoryId` (int) - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/manga/{mangaId}/library` - **What it does:** Add manga to library - **Expected request:** Path parameter: `mangaId` (int) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `DELETE /api/v1/manga/{mangaId}/library` - **What it does:** Remove manga from library - **Expected request:** Path parameter: `mangaId` (int) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `PATCH /api/v1/manga/{mangaId}/meta` - **What it does:** Add/update metadata to a manga - **Expected request:** Path parameter: `mangaId` (int), form params: `key` (string), `value` (string) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `GET /api/v1/manga/{mangaId}/chapters` - **What it does:** Get manga chapter list - **Expected request:** Path parameter: `mangaId` (int), optional query param: `onlineFetch` (boolean) - **Expected response:** JSON array of ChapterDataClass objects (HTTP 200) or HTTP 404 (Not Found) **Response JSON Example:** ```json [ { "id": 100, "url": "/chapter/12345/1000", "name": "Chapter 1000: The Final Battle", "uploadDate": 1699999999, "chapterNumber": 1000.0, "scanlator": "MangaDex", "mangaId": 1, "read": false, "bookmarked": false, "lastPageRead": 0, "lastReadAt": 0, "index": 1000, "fetchedAt": 1699999999, "realUrl": "https://mangadex.org/chapter/12345/1000", "downloaded": false, "pageCount": 18, "chapterCount": 1000, "meta": {} }, { "id": 99, "url": "/chapter/12345/999", "name": "Chapter 999: The Beginning", "uploadDate": 1699999998, "chapterNumber": 999.0, "scanlator": "MangaDex", "mangaId": 1, "read": true, "bookmarked": true, "lastPageRead": 15, "lastReadAt": 1699999999, "index": 999, "fetchedAt": 1699999998, "realUrl": "https://mangadex.org/chapter/12345/999", "downloaded": true, "pageCount": 15, "chapterCount": 1000, "meta": {} } ] ``` ### `POST /api/v1/manga/{mangaId}/chapter/batch` - **What it does:** Batch update chapters (mark as read, bookmark, etc.) - **Expected request:** Path parameter: `mangaId` (int), body: MangaChapterBatchEditInput object **Request JSON Example (Mark as read and bookmark):** ```json { "chapterIds": [100, 101, 102], "chapterIndexes": null, "change": { "isRead": true, "isBookmarked": true, "lastPageRead": null, "delete": null } } ``` **Request JSON Example (Mark previous as read):** ```json { "chapterIds": [100], "chapterIndexes": null, "change": { "isRead": null, "isBookmarked": null, "lastPageRead": null, "delete": null, "markPrevRead": true } } ``` **Request JSON Example (Delete downloaded chapters):** ```json { "chapterIds": [100, 101], "change": { "isRead": null, "isBookmarked": null, "lastPageRead": null, "delete": true } } ``` - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/manga/{mangaId}/chapter/{chapterIndex}` - **What it does:** Get a specific chapter with pages - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int) - **Expected response:** JSON ChapterDataClass object (HTTP 200) or HTTP 404 (Not Found) ### `PATCH /api/v1/manga/{mangaId}/chapter/{chapterIndex}` - **What it does:** Modify chapter (read status, bookmark, etc.) - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int), form params: `read` (boolean, optional), `bookmarked` (boolean, optional), `markPrevRead` (boolean, optional), `lastPageRead` (int, optional) **Form Data Example:** ``` read=true bookmarked=false lastPageRead=5 markPrevRead=true ``` - **Expected response:** HTTP 200 (OK) ### `PUT /api/v1/manga/{mangaId}/chapter/{chapterIndex}` - **What it does:** Modify chapter (same as PATCH) - **Expected request:** Same as PATCH - **Expected response:** HTTP 200 (OK) ### `DELETE /api/v1/manga/{mangaId}/chapter/{chapterIndex}` - **What it does:** Delete downloaded chapter files - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `PATCH /api/v1/manga/{mangaId}/chapter/{chapterIndex}/meta` - **What it does:** Add metadata to a chapter - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int), form params: `key` (string), `value` (string) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `GET /api/v1/manga/{mangaId}/chapter/{chapterIndex}/page/{index}` - **What it does:** Get a chapter page image - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int), `index` (int), optional query params: `updateProgress` (boolean), `format` (string), `opds` (boolean) - **Expected response:** Image file with HTTP 200 or HTTP 404 (Not Found) --- ## Chapter Endpoints ### `POST /api/v1/chapter/batch` - **What it does:** Batch update chapters across any manga - **Expected request:** Body: ChapterBatchEditInput object **Request JSON Example:** ```json { "chapterIds": [100, 101, 102, 200, 201], "change": { "isRead": true, "isBookmarked": false, "lastPageRead": null, "delete": null } } ``` - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/chapter/{chapterId}/download` - **What it does:** Download chapter as CBZ file - **Expected request:** Path parameter: `chapterId` (int), optional query param: `markAsRead` (boolean) - **Expected response:** CBZ file stream with HTTP 200 or HTTP 404 (Not Found) **Response Headers:** ``` Content-Type: application/vnd.comicbook+zip Content-Disposition: attachment; filename="One Piece - Chapter 1000.cbz" Content-Length: 1234567 ``` ### `HEAD /api/v1/chapter/{chapterId}/download` - **What it does:** Get CBZ file metadata (HEAD request) - **Expected request:** Path parameter: `chapterId` (int) - **Expected response:** HTTP headers with file info, or HTTP 404 (Not Found) --- ## Category Endpoints ### `GET /api/v1/category` - **What it does:** Get list of all categories - **Expected request:** No parameters required - **Expected response:** JSON array of CategoryDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "id": 1, "order": 0, "name": "Favorites", "default": false, "size": 10, "includeInUpdate": "INCLUDE", "includeInDownload": "INCLUDE", "meta": {} }, { "id": 2, "order": 1, "name": "Reading", "default": false, "size": 5, "includeInUpdate": "INCLUDE", "includeInDownload": "EXCLUDE", "meta": {} }, { "id": 3, "order": 2, "name": "Default", "default": true, "size": 100, "includeInUpdate": "INCLUDE", "includeInDownload": "INCLUDE", "meta": {} } ] ``` ### `POST /api/v1/category` - **What it does:** Create a new category - **Expected request:** Form param: `name` (string) **Form Data Example:** ``` name=My Category ``` - **Expected response:** HTTP 200 (OK) or HTTP 400 (Bad Request) ### `PATCH /api/v1/category/reorder` - **What it does:** Reorder categories - **Expected request:** Form params: `from` (int), `to` (int) **Form Data Example:** ``` from=0 to=2 ``` - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/category/{categoryId}` - **What it does:** Get manga in a category - **Expected request:** Path parameter: `categoryId` (int) - **Expected response:** JSON array of MangaDataClass objects (HTTP 200) ### `PATCH /api/v1/category/{categoryId}` - **What it does:** Modify a category - **Expected request:** Path parameter: `categoryId` (int), optional form params: `name` (string), `default` (boolean), `includeInUpdate` (int), `includeInDownload` (int) **Form Data Example:** ``` name=Updated Name default=false includeInUpdate=1 includeInDownload=0 ``` - **Expected response:** HTTP 200 (OK) ### `DELETE /api/v1/category/{categoryId}` - **What it does:** Delete a category - **Expected request:** Path parameter: `categoryId` (int) - **Expected response:** HTTP 200 (OK) ### `PATCH /api/v1/category/{categoryId}/meta` - **What it does:** Add metadata to a category - **Expected request:** Path parameter: `categoryId` (int), form params: `key` (string), `value` (string) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) --- ## Backup Endpoints ### `POST /api/v1/backup/import` - **What it does:** Restore a backup from request body - **Expected request:** Body: Tachiyomi protobuf backup data (binary) - **Expected response:** HTTP 200 (OK) ### `POST /api/v1/backup/import/file` - **What it does:** Restore a backup from file upload - **Expected request:** Multipart form data with file named "backup.proto.gz" - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `POST /api/v1/backup/validate` - **What it does:** Validate a backup from request body - **Expected request:** Body: Tachiyomi protobuf backup data (binary) - **Expected response:** JSON ValidationResult object (HTTP 200) **Response JSON Example:** ```json { "missingSources": ["com.source.example"], "missingTrackers": ["myanimelist"], "isValid": false } ``` ### `POST /api/v1/backup/validate/file` - **What it does:** Validate a backup from file upload - **Expected request:** Multipart form data with file named "backup.proto.gz" - **Expected response:** JSON ValidationResult object (HTTP 200) ### `GET /api/v1/backup/export` - **What it does:** Create a backup (returns as body) - **Expected request:** No parameters required - **Expected response:** Binary stream (protobuf) with HTTP 200 **Response Headers:** ``` Content-Type: application/octet-stream ``` ### `GET /api/v1/backup/export/file` - **What it does:** Create a backup (returns as file download) - **Expected request:** No parameters required - **Expected response:** Binary stream (protobuf) as file download with HTTP 200 **Response Headers:** ``` Content-Type: application/octet-stream Content-Disposition: attachment; filename="suwayomi_backup_20240101.proto.gz" ``` --- ## Download Endpoints ### `GET /api/v1/downloads/start` - **What it does:** Start the downloader - **Expected request:** No parameters required - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/downloads/stop` - **What it does:** Stop the downloader - **Expected request:** No parameters required - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/downloads/clear` - **What it does:** Clear the download queue - **Expected request:** No parameters required - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/download/{mangaId}/chapter/{chapterIndex}` - **What it does:** Queue a chapter for download - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int) - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `DELETE /api/v1/download/{mangaId}/chapter/{chapterIndex}` - **What it does:** Remove a chapter from download queue - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int) - **Expected response:** HTTP 200 (OK) ### `PATCH /api/v1/download/{mangaId}/chapter/{chapterIndex}/reorder/{to}` - **What it does:** Reorder a chapter in download queue - **Expected request:** Path parameters: `mangaId` (int), `chapterIndex` (int), `to` (int) - **Expected response:** HTTP 200 (OK) ### `POST /api/v1/download/batch` - **What it does:** Queue multiple chapters for download - **Expected request:** Body: EnqueueInput object **Request JSON Example:** ```json { "chapterIds": [100, 101, 102, 103, 104] } ``` - **Expected response:** HTTP 200 (OK) ### `DELETE /api/v1/download/batch` - **What it does:** Remove multiple chapters from download queue - **Expected request:** Body: EnqueueInput object **Request JSON Example:** ```json { "chapterIds": [100, 101, 102] } ``` - **Expected response:** HTTP 200 (OK) --- ## Update Endpoints ### `GET /api/v1/update/recentChapters/{pageNum}` - **What it does:** Get recently updated chapters - **Expected request:** Path parameter: `pageNum` (int) - **Expected response:** JSON PagedMangaChapterListDataClass object (HTTP 200) **Response JSON Example:** ```json { "mangaList": [ { "id": 1, "mangaId": 1, "mangaTitle": "One Piece", "mangaThumbnail": "https://example.com/cover.jpg", "chapters": [ { "id": 100, "url": "/chapter/1/100", "name": "Chapter 100", "uploadDate": 1699999999, "chapterNumber": 100.0, "scanlator": "MangaDex", "read": false, "bookmarked": false, "lastPageRead": 0, "lastReadAt": 0, "index": 100, "fetchedAt": 1699999999, "downloaded": false } ] } ], "hasNextPage": true } ``` ### `POST /api/v1/update/fetch` - **What it does:** Start library update (optionally for specific category) - **Expected request:** Optional form param: `categoryId` (int) **Form Data Examples:** ``` (Updates entire library) ``` or ``` categoryId=1 ``` - **Expected response:** HTTP 200 (OK) or HTTP 400 (Bad Request) ### `POST /api/v1/update/reset` - **What it does:** Stop and reset the updater - **Expected request:** No parameters required - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/update/summary` - **What it does:** Get updater status summary - **Expected request:** No parameters required - **Expected response:** JSON UpdateStatus object (HTTP 200) **Response JSON Example:** ```json { "status": "Running", "progress": { "current": 5, "total": 100 }, "pending": 0, "running": true, "mangaDone": 5, "mangaTotal": 100, "chapterDone": 50, "chapterAdded": 10 } ``` --- ## Track Endpoints ### `GET /api/v1/track/list` - **What it does:** List all supported trackers - **Expected request:** No parameters required - **Expected response:** JSON array of TrackerDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "id": 1, "name": "MyAnimeList", "icon": "/track/thumbnail/1", "isLogin": false, "authUrl": "https://myanimelist.net/api/v2/oauth/authorize" }, { "id": 2, "name": "AniList", "icon": "/track/thumbnail/2", "isLogin": true, "authUrl": "https://anilist.co/api/v2/oauth/authorize" } ] ``` ### `POST /api/v1/track/login` - **What it does:** Login to a tracker - **Expected request:** Body: LoginInput object **Request JSON Example:** ```json { "trackerId": 1, "callbackUrl": "suwayomi://auth/myanimelist", "username": "myuser", "password": "mypassword" } ``` - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `POST /api/v1/track/logout` - **What it does:** Logout from a tracker - **Expected request:** Body: LogoutInput object **Request JSON Example:** ```json { "trackerId": 1 } ``` - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) ### `POST /api/v1/track/search` - **What it does:** Search for manga on a tracker - **Expected request:** Body: SearchInput object **Request JSON Example:** ```json { "trackerId": 1, "title": "One Piece" } ``` - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) **Response JSON Example:** ```json [ { "trackerId": 1, "remoteId": "12345", "remoteUrl": "https://myanimelist.net/manga/12345", "title": "One Piece", "coverUrl": "https://example.com/cover.jpg", "summary": "A story about pirates...", "match": 95 } ] ``` ### `POST /api/v1/track/bind` - **What it does:** Bind a manga to a track record - **Expected request:** Query params: `mangaId` (int), `trackerId` (int), `remoteId` (string), `private` (boolean) **Request URL Example:** ``` /api/v1/track/bind?mangaId=1&trackerId=1&remoteId=12345&private=false ``` - **Expected response:** HTTP 200 (OK) ### `POST /api/v1/track/update` - **What it does:** Update track record with tracker - **Expected request:** Body: UpdateInput object **Request JSON Example (Update reading progress):** ```json { "recordId": 1, "status": 1, "lastChapterRead": 100, "scoreString": "9.5", "startDate": null, "finishDate": null, "private": false } ``` **Request JSON Example (Unbind from tracker):** ```json { "recordId": 1, "unbind": true } ``` **Status values:** - 1 = Reading - 2 = Completed - 3 = On Hold - 4 = Dropped - 6 = Plan to Read - **Expected response:** HTTP 200 (OK) ### `GET /api/v1/track/{trackerId}/thumbnail` - **What it does:** Get tracker thumbnail image - **Expected request:** Path parameter: `trackerId` (int) - **Expected response:** Image file with HTTP 200 or HTTP 404 (Not Found) --- ## Global Meta Endpoints ### `GET /api/v1/meta` - **What it does:** Get all global metadata - **Expected request:** No parameters required - **Expected response:** HTTP 200 (OK) with JSON key-value pairs **Response JSON Example:** ```json { "customKey": "customValue", "lastSync": "1234567890" } ``` ### `PATCH /api/v1/meta` - **What it does:** Add/update global metadata - **Expected request:** Form params: `key` (string), `value` (string) **Form Data Example:** ``` key=lastSync value=1234567890 ``` - **Expected response:** HTTP 200 (OK) or HTTP 404 (Not Found) --- ## Settings Endpoints ### `GET /api/v1/settings/about` - **What it does:** Get application information - **Expected request:** No parameters required - **Expected response:** JSON AboutDataClass object (HTTP 200) **Response JSON Example:** ```json { "name": "Suwayomi-Server", "version": "0.7.0", "revision": "7a1b2c3", "buildType": "release", "buildTime": 1704067200, "github": "https://github.com/Suwayomi/Suwayomi-Server", "discord": "https://discord.gg/Suwayomi" } ``` ### `GET /api/v1/settings/check-update` - **What it does:** Check for application updates - **Expected request:** No parameters required - **Expected response:** JSON array of UpdateDataClass objects (HTTP 200) **Response JSON Example:** ```json [ { "name": "0.8.0", "tagName": "v0.8.0", "body": "## Bug fixes\n- Fixed bug #123\n- Improved performance", "htmlUrl": "https://github.com/Suwayomi/Suwayomi-Server/releases/tag/v0.8.0", "prerelease": false } ] ``` --- ## WebView Endpoints ### `GET /api/v1/webview` - **What it does:** Open webview interface - **Expected request:** Optional query param: `lang` (string) **Request URL Example:** ``` /api/v1/webview?lang=en ``` - **Expected response:** HTML page (HTTP 200) --- ## WebSocket Endpoints The server provides WebSocket connections for real-time communication and updates. **Note:** Actions like starting/stopping downloads or updates are done via HTTP endpoints (`GET /api/v1/downloads/start`, `GET /api/v1/downloads/stop`, `POST /api/v1/update/fetch`, etc.), not through WebSocket messages. WebSocket connections are primarily for receiving real-time status updates. --- ### `WS /api/v1/downloads` - **What it does:** Real-time download queue status updates - **Expected request:** WebSocket connection with authentication #### Messages You Can Send to Server: **Request current download status:** ``` STATUS ``` - **Expected Response from Server:** ```json { "status": "Started", "queue": [ { "mangaId": 1, "chapterId": 100, "chapterIndex": 100, "mangaTitle": "One Piece", "chapterTitle": "Chapter 100", "state": "DOWNLOADING", "progress": 50, "tries": 0, "downloadSpeed": 1024000, "errorMessage": null } ] } ``` **State values:** `Queued`, `Downloading`, `Completed`, `Error` **Invalid command response:** ``` Invalid command. Supported commands are: - STATUS sends the current download status ``` #### Messages Received from Server (Automatic Updates): The server automatically sends status updates when: - A download starts/completes/fails - Queue changes (add/remove/reorder) **Example update:** ```json { "status": "Started", "queue": [ { "mangaId": 1, "chapterId": 100, "chapterIndex": 100, "mangaTitle": "One Piece", "chapterTitle": "Chapter 100", "state": "Completed", "progress": 100, "tries": 0, "downloadSpeed": 0, "errorMessage": null } ], "downloads": [ { "downloadQueueItem": { "mangaId": 1, "chapterId": 101, "chapterIndex": 101, "mangaTitle": "One Piece", "chapterTitle": "Chapter 101", "state": "Downloading", "progress": 25, "tries": 0 }, "downloadSpeed": 1024000 } ] } ``` --- ### `WS /api/v1/update` - **What it does:** Real-time library update progress - **Expected request:** WebSocket connection with authentication #### Messages You Can Send to Server: **Request current update status:** ``` STATUS ``` - **Expected Response from Server:** ```json { "status": "Running", "pending": 0, "running": true, "mangaDone": 5, "mangaTotal": 100, "chapterDone": 50, "chapterAdded": 10, "progress": { "current": 5, "total": 100 }, "errors": [] } ``` **Status values:** `Idle`, `Running`, `Completed`, `Stopped` **Invalid command response:** ``` Invalid command. Supported commands are: - STATUS sends the current update status ``` #### Messages Received from Server (Automatic Updates): The server automatically sends status updates during library updates. **Example update:** ```json { "status": "Running", "pending": 95, "running": true, "mangaDone": 5, "mangaTotal": 100, "chapterDone": 50, "chapterAdded": 10, "progress": { "current": 5, "total": 100 }, "errors": [] } ``` --- ### `WS /api/v1/webview` - **What it does:** Real-time webview communication for embedded browser functionality - **Expected request:** WebSocket connection with authentication #### Messages You Can Send to Server: **Load a URL in the webview:** ```json { "type": "loadUrl", "url": "https://example.com", "width": 1920, "height": 1080 } ``` - **Expected Response:** None (webview loads the URL) **Resize the webview:** ```json { "type": "resize", "width": 800, "height": 600 } ``` - **Expected Response:** None **Send a JavaScript event:** ```json { "type": "event", "eventType": "click", "clickX": 100.5, "clickY": 200.75, "button": 0, "ctrlKey": false, "shiftKey": false, "altKey": false, "metaKey": false } ``` - **Expected Response:** None (event is sent to the webview) **Keyboard event:** ```json { "type": "event", "eventType": "keydown", "key": "Enter", "code": "Enter", "ctrlKey": true } ``` - **Expected Response:** None **Scroll event:** ```json { "type": "event", "eventType": "wheel", "deltaY": -100, "clientX": 500, "clientY": 500 } ``` - **Expected Response:** None **Paste text to webview:** ```json { "type": "paste", "data": "pasted text content" } ``` - **Expected Response:** None **Copy from webview:** ```json { "type": "copy" } ``` - **Expected Response:** None **Ping to check connection:** ```json { "type": "ping" } ``` - **Expected Response from Server:** ```json { "type": "pong" } ``` #### Messages Received from Server: The server sends various responses based on webview interactions: **Pong response:** ```json { "type": "pong" } ``` **JavaScript console output (example):** ```json { "type": "console", "level": "log", "message": "Page loaded successfully" } ``` **Page load events:** ```json { "type": "loadStart", "url": "https://example.com" } ``` ```json { "type": "loadFinish", "url": "https://example.com", "httpStatusCode": 200 } ``` **Error events:** ```json { "type": "error", "description": "Failed to load resource", "url": "https://example.com/broken-link" } ``` --- ## GraphQL Endpoint **Base URL:** `http://localhost:4567/api/graphql` The server also provides a GraphQL API for more flexible queries and subscriptions. See the GraphQL schema for available queries, mutations, and subscriptions. --- ## OPDS Endpoints The OPDS API provides catalog feeds for integration with OPDS-compatible readers. **Base URL:** `http://localhost:4567/api/opds/v1.2/` ### `GET /api/opds/v1.2/opds/v1.2` - **What it does:** OPDS Catalog Root Feed (Navigation) - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) **Response XML Example:** ```xml Suwayomi Library Your manga library Explore Browse sources ``` ### `GET /api/opds/v1.2/search` - **What it does:** OPDS Search Description Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/explore` - **What it does:** Explore Sources Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/history` - **What it does:** Reading History Acquisition Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library-updates` - **What it does:** Library Updates Acquisition Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/series` - **What it does:** All Series in Library / Search Results Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/sources` - **What it does:** Library Sources Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/source/{sourceId}` - **What it does:** Library Source-Specific Series Acquisition Feed - **Expected request:** Path parameter: `sourceId` (long) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/categories` - **What it does:** Library Categories Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/genres` - **What it does:** Library Genres Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/statuses` - **What it does:** Library Status Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/library/languages` - **What it does:** Library Content Languages Navigation Feed - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/sources` - **What it does:** All Sources Navigation Feed (Explore) - **Expected request:** No parameters required - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/source/{sourceId}` - **What it does:** Source-Specific Series Acquisition Feed (Explore) - **Expected request:** Path parameter: `sourceId` (long) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/series/{seriesId}/chapters` - **What it does:** Series Chapters Acquisition Feed - **Expected request:** Path parameter: `seriesId` (int) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/series/{seriesId}/chapter/{chapterIndex}/metadata` - **What it does:** Chapter Metadata Acquisition Feed - **Expected request:** Path parameters: `seriesId` (int), `chapterIndex` (int) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/category/{categoryId}` - **What it does:** Category-Specific Series Acquisition Feed - **Expected request:** Path parameter: `categoryId` (int) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/genre/{genre}` - **What it does:** Genre-Specific Series Acquisition Feed - **Expected request:** Path parameter: `genre` (string) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/status/{statusId}` - **What it does:** Status-Specific Series Acquisition Feed - **Expected request:** Path parameter: `statusId` (int) - **Expected response:** XML OPDS feed (HTTP 200) ### `GET /api/opds/v1.2/language/{langCode}` - **What it does:** Language-Specific Series Acquisition Feed - **Expected request:** Path parameter: `langCode` (string) - **Expected response:** XML OPDS feed (HTTP 200) --- ## Notes - The server runs on port 4567 by default - All JSON responses use UTF-8 encoding - File uploads use multipart/form-data format - WebSocket endpoints provide real-time updates for downloads, updates, and webview - WebSocket messages are JSON formatted (except STATUS command which is plain text) - Image responses include caching headers with 1-day max-age for thumbnails and pages - OPDS feeds use Atom XML format for catalog navigation and acquisition - Actions like start/stop for downloads and updates are done via HTTP endpoints, not WebSocket - WebSocket endpoints only support "STATUS" command to query current status - See [Authentication](#authentication) section for details on how to authenticate with the API