Files
Suwayomi-Server/API_DOCUMENTATION.md
achmad 06e93fd7bd
CI build / Validate Gradle Wrapper (push) Successful in 2m30s
CI build / jlink (linux-x64, ubuntu-latest) (push) Failing after 1m12s
CI build / Build Jar (push) Failing after 2m18s
CI build / jlink (macOS-arm64, macos-15) (push) Has been cancelled
CI build / jlink (macOS-x64, macos-15-intel) (push) Has been cancelled
CI build / jlink (windows-x64, windows-latest) (push) Has been cancelled
CI build / Make linux-assets release (push) Has been cancelled
CI build / Make appimage release (push) Has been cancelled
CI build / Make debian-all release (push) Has been cancelled
CI build / Make linux-x64 release (push) Has been cancelled
CI build / Make macOS-arm64 release (push) Has been cancelled
CI build / Make macOS-x64 release (push) Has been cancelled
CI build / Make windows-x64 release (push) Has been cancelled
CI build / release (push) Has been cancelled
chore: add Docker build config, scripts, and private registry setup
2026-05-10 11:45:24 +07:00

1734 lines
42 KiB
Markdown

# 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 <base64-encoded-credentials>
```
### 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
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:opds="http://opds-spec.org/2010/catalog">
<title>Suwayomi</title>
<link rel="start" href="/api/opds/v1.2/opds/v1.2" type="application/atom+xml"/>
<link rel="self" href="/api/opds/v1.2/opds/v1.2" type="application/atom+xml"/>
<entry>
<title>Library</title>
<link href="/api/opds/v1.2/library/series" type="application/atom+xml;profile=opds-catalog"/>
<content type="text">Your manga library</content>
</entry>
<entry>
<title>Explore</title>
<link href="/api/opds/v1.2/sources" type="application/atom+xml;profile=opds-catalog"/>
<content type="text">Browse sources</content>
</entry>
</feed>
```
### `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