Skip to content

HTTP Server

startServer launches a Node http.Server that exposes the SDK’s operations as HTTP routes: /search, /content, /stream, /tracks, and (optionally) /proxy. Pass the providers you want to expose, plus optional flags for the proxy, a bearer token, and a cache.

import { HttpClient, AllmangaProvider, GogoanimeProvider, startServer } from 'anime-sdk';
const server = startServer({
providers: [
new AllmangaProvider(new HttpClient()),
new GogoanimeProvider(new HttpClient({ timeoutMs: 10_000 })),
],
port: 3000, // default: 3000
proxy: true, // optional: enables /proxy and rewrites stream + subtitle URLs through it
});
// server is an http.Server: you can call server.close() to shut it down

The server logs anime-sdk server listening on http://localhost:3000 when ready.

All routes are GET only. Errors return { error: string } with the appropriate HTTP status.

Search for shows or manga.

ParamRequiredDescription
qSearch query string
providerProvider ID: allmanga, gogoanime, anikoto, mangadex, weebcentral, or mangapill
Terminal window
curl 'localhost:3000/search?q=Frieren&provider=allmanga'

Response: IMediaSearchResult[]

[
{
"id": "frieren-beyond-journeys-end",
"title": "Frieren: Beyond Journey's End",
"thumbnailUrl": "https://...",
"catalogType": "ANIME",
"providerId": "allmanga",
"availableLanguages": ["sub", "dub"]
}
]

List episodes for a show or chapters for a manga. One call returns the unified, language-agnostic list: each unit advertises its translations.

ParamRequiredDescription
mediaIdid from a search result
providerProvider ID
Terminal window
curl 'localhost:3000/content?mediaId=frieren-beyond-journeys-end&provider=allmanga'

Response: IContentUnit[]

[
{
"id": "frieren-beyond-journeys-end/1",
"title": "Episode 1",
"number": 1,
"availableLanguages": ["sub", "dub"]
}
]

Resolve a direct stream URL for an episode or image URLs for a manga chapter. Pick the translation here (if applicable).

ParamRequiredDescription
unitIdid from a content result
providerProvider ID
languagesub | dub | raw
Terminal window
curl 'localhost:3000/stream?unitId=frieren-beyond-journeys-end%2F1&provider=allmanga&language=dub'

Response: ResolvedMediaStream

Anime response:

{
"type": "video",
"streams": [
{
"sourceUrl": "https://a4.mp4upload.com:183/d/.../video.mp4",
"isHLS": false,
"quality": "auto",
"language": "dub",
"headers": { "Referer": "https://mp4upload.com/" },
"subtitles": [
{
"url": "https://cdn.example.com/subs/en.vtt",
"language": "en",
"label": "English",
"format": "vtt"
}
]
}
]
}

Manga response:

{
"type": "manga",
"pages": {
"imageUrls": ["https://...", "https://..."],
"headers": { "Referer": "https://..." }
}
}

When proxy: true, both sourceUrl, anime subtitle urls, and manga imageUrls are rewritten through /proxy automatically.

Inspect subtitle + quality availability for a unit without resolving the playable stream. Useful for populating a subtitle selector before the user hits play.

ParamRequiredDescription
unitIdid from a content result
providerProvider ID
languagesub | dub | raw
Terminal window
curl 'localhost:3000/tracks?unitId=...&provider=animeparadise&language=sub'

Response: IUnitTracks

{
"subtitles": [
{
"url": "http://localhost:3000/proxy?url=...&ct=text%2Fvtt",
"label": "English",
"language": "en",
"format": "vtt"
}
],
"qualities": ["auto"]
}

Returns 501 if the provider doesn’t implement fetchUnitTracks: read subtitles from /stream’s response instead.

Resolve and download an episode to the server’s disk, streaming live progress events via Server-Sent Events.

ParamRequiredDescription
unitIdid from a content result
providerProvider ID
languagesub | dub | raw
Terminal window
curl -N 'localhost:3000/download/video/progress?unitId=...&provider=allmanga&language=sub'

Events arrive as data: <JSON>\n\n. Three event shapes:

// While downloading:
{ "type": "progress", "phase": "downloading", "detail": "Segment 12/45" }
{ "type": "progress", "phase": "muxing", "detail": "Muxing segments to MP4 via ffmpeg" }
// On success: retrieve the file with /download/video/file?token=<token>:
{ "type": "complete", "token": "550e8400-e29b-41d4-a716-446655440000" }
// On failure:
{ "type": "error", "message": "downloadVideo exhausted all candidates…" }

Progress phase values in order: resolvingdownloadingmuxingcomplete.

Retrieve a completed video download by token. One-time: the file is deleted after the first successful response.

ParamRequiredDescription
tokenToken from a /download/video/progress complete event

Response: video/mp4 with Content-Disposition: attachment. Tokens expire after 10 minutes.

Terminal window
curl -OJ 'localhost:3000/download/video/file?token=550e8400-…'

GET /download/manga/chapter/progress (SSE)

Section titled “GET /download/manga/chapter/progress (SSE)”

Same as /download/video/progress but for manga chapters. Downloads all pages and packages them as a ZIP archive.

ParamRequiredDescription
unitIdid from a content result
providerProvider ID
{ "type": "progress", "downloaded": 8, "total": 24 }
{ "type": "complete", "token": "" }
{ "type": "error", "message": "" }

Retrieve a completed chapter ZIP by token (one-time, 10-minute expiry).

Response: application/zip with Content-Disposition: attachment.

Download a video without progress streaming. Resolves the stream, downloads to a server temp file via downloadVideo, then serves the completed MP4 in one response. Suitable for programmatic use when SSE is inconvenient.

ParamRequiredDescription
unitIdUnit ID
providerProvider ID
languagesub | dub | raw

Response: video/mp4 with Content-Disposition: attachment. Timeout: 20 minutes.

Download a single manga page image.

ParamRequiredDescription
unitIdUnit ID
providerProvider ID
page0-based page index (default: 0)

Response: image file (image/jpeg, image/png, etc.) with Content-Disposition: attachment.

Download an entire chapter as a ZIP without progress streaming.

ParamRequiredDescription
unitIdUnit ID
providerProvider ID

Response: application/zip with Content-Disposition: attachment.

Lock the server behind a bearer token:

startServer({
providers: [new AllmangaProvider(new HttpClient())],
port: 3000,
auth: { token: process.env.API_TOKEN! },
});

Clients include the token in the Authorization header:

Terminal window
curl -H 'Authorization: Bearer mysecret' 'localhost:3000/search?q=Naruto&provider=allmanga'

Requests without a valid token receive 401 Unauthorized. Skip auth entirely for local use.

startServer accepts an optional cache that memoises provider calls across /search, /content, /stream, and /tracks. The contract is two methods:

interface SdkCache {
get(key: string): unknown | Promise<unknown>;
set(key: string, value: unknown): void | Promise<void>;
}

A plain Map satisfies it. Bring whatever store you like (Redis, SQLite, edge KV):

const store = new Map();
const cache = {
get: (key) => store.get(key),
set: (key, value) => void store.set(key, value),
};
startServer({
providers: [new AllmangaProvider(new HttpClient())],
port: 3000,
proxy: true,
cache,
});

Keys are namespaced so different endpoints can have different policies:

PrefixKey shapeNotes
search:search:<providerId>:<query>Stable; safe to cache for long.
content:content:<providerId>:<mediaId>Stable; safe to cache for long.
stream:stream:<providerId>:<unitId>:<lang>Stream URLs may carry signed expiries: apply a short TTL or skip.
tracks:tracks:<providerId>:<unitId>:<lang>Cheap to compute when the provider supports it; cache freely.

get returns undefined for a miss; any other value (including null) is treated as a hit and served as-is. To skip caching a specific endpoint, inspect the prefix in your set and bail out.

StatusMeaning
400Missing or unknown query parameter
401Missing or invalid bearer token
404Unknown route
405Non-GET request
500Provider threw an error (message is included in error field)

The server is the bridge for any client that can’t run Node directly.

Python:

import httpx
base = 'http://localhost:3000'
headers = {'Authorization': 'Bearer mysecret'}
shows = httpx.get(f'{base}/search', params={'q': 'Frieren', 'provider': 'allmanga'}, headers=headers).json()
eps = httpx.get(f'{base}/content', params={'mediaId': shows[0]['id'], 'provider': 'allmanga'}, headers=headers).json()
data = httpx.get(f'{base}/stream', params={'unitId': eps[0]['id'], 'provider': 'allmanga'}, headers=headers).json()
url = data['streams'][0]['sourceUrl']

Swift (iOS):

let url = URL(string: "http://localhost:3000/search?q=Frieren&provider=allmanga")!
let (data, _) = try await URLSession.shared.data(from: url)
let shows = try JSONDecoder().decode([MediaSearchResult].self, from: data)

Kotlin / Android:

val client = OkHttpClient()
val request = Request.Builder()
.url("http://10.0.2.2:3000/search?q=Frieren&provider=allmanga")
.build()
val response = client.newCall(request).execute()

Note: in Android emulators, localhost on the host machine is reachable at 10.0.2.2.