📖 Table of Contents
✨ Features
- Built-in HLS Proxy: Direct playback of Cloudflare-protected
.m3u8playlists without passing custom headers in your client. - Subtitle Parsing: Soft-subs are extracted, proxied, and parsed cleanly.
- Edge Optimized: Built on Next.js Edge runtime for massive concurrency.
- Cross-Origin Ready: Bypasses CORS effortlessly.
🚀 Quick Start
- Clone & Install
git clone https://github.com/your-username/animetsu-api.git cd animetsu-api npm install - Run Development Server
npm run devServer runs on
http://localhost:3000
☁️ Deployment
This project is optimized for the Edge and deploys seamlessly to Cloudflare Pages using @cloudflare/next-on-pages.
Deploy via Cloudflare Dashboard (Recommended)
- Go to your Cloudflare Dashboard.
- Navigate to Workers & Pages -> Create application -> Pages -> Connect to Git.
- Select your repository and use the following build settings:
- Framework preset:
Next.js - Build command:
npx @cloudflare/next-on-pages - Build output directory:
.vercel/output/static
- Framework preset:
Manual Deployment Guide
-
Install Wrangler CLI globally (if not already)
npm install -g wrangler -
Login to Cloudflare
wrangler login -
Build the Project for Cloudflare Pages
npx @cloudflare/next-on-pages -
Deploy to Cloudflare Pages
npx wrangler pages deploy .vercel/output/static --project-name animetsu-api
🎮 Player Integration
1. JWPlayer Integration (Recommended)
JWPlayer handles .m3u8 qualities natively and parses subtitles perfectly.
<!-- Include JWPlayer Library -->
<script src="https://content.jwplatform.com/libraries/KB5zFt7A.js"></script>
<div id="player"></div>
// 1. Fetch Watch Data
const res = await fetch(`http://localhost:3000/api/watch?id=6989bf2429cf95f4eb040650&ep=1&server=pahe&source_type=sub`);
const json = await res.json();
const watchData = json.data;
// 2. Map subtitles to JWPlayer Track format
const tracks = watchData.subtitles?.map(sub => ({
file: sub.url, // Proxied soft-subs!
label: sub.label,
kind: "captions",
"default": sub.label.toLowerCase().includes("english")
})) || [];
// 3. Initialize JWPlayer
jwplayer("player").setup({
playlist: [{
file: watchData.sources[0].proxy_url, // Automatically detects 1080p, 720p, etc.
tracks: tracks
}],
width: "100%",
aspectratio: "16:9",
autostart: true
});
</details>
2. Custom HTML5 Player Integration (Aniflix)
If you are building a highly customized UI (like Aniflix Player) from scratch using raw HTML/CSS, use hls.js.
import Hls from "hls.js";
const videoElement = document.querySelector("#vid");
const res = await fetch(`http://localhost:3000/api/watch?id=6989bf2429cf95f4eb040650&ep=1&server=pahe&source_type=sub`);
const watchData = (await res.json()).data;
const streamUrl = watchData.sources[0].proxy_url;
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(streamUrl);
hls.attachMedia(videoElement);
// MANUALLY HOOKING UP QUALITY BUTTONS:
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
// Force switch quality
document.querySelector("#btn-1080p").onclick = () => {
hls.currentLevel = data.levels.findIndex(l => l.height === 1080);
};
});
}
// INJECTING SUBTITLES
if (watchData.subtitles) {
watchData.subtitles.forEach(sub => {
const track = document.createElement("track");
track.kind = "subtitles";
track.label = sub.label;
track.srclang = sub.lang.substring(0, 2).toLowerCase();
track.src = sub.url;
if (sub.lang.toLowerCase().includes("english")) track.default = true;
videoElement.appendChild(track);
});
}
</details>
📚 API Reference
Here are the primary endpoints documented with exact schemas and cURL examples.
Fetch detailed metadata about a specific anime.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | string | Yes | The Anime ID |
Example cURL:
curl -s "http://localhost:3000/api/anime/6989bf2429cf95f4eb040650"
</details>
<details>
<summary><b>1b. Get Anime Info via AniList ID <code>/api/anime/anilist/[id]</code></b></summary>
Fetch detailed anime metadata using a dynamic AniList ID.
This endpoint performs an automated background mapping:
- Queries the AniList GraphQL API to fetch anime titles (romaji, english, native, userPreferred), synonyms, release year, format, and episode count.
- Executes parallel search queries on the Animetsu API using Romaji and English titles.
- Applies a multi-factor scoring algorithm checking normalized title similarity, release year compatibility (exact year vs. ±1 year crossing season boundaries), format matches (TV, MOVIE, OVA, ONA, SPECIAL), and episode counts.
- Instantly resolves the target Animetsu ID and retrieves the detailed info, returning the payload as a drop-in replacement with additional mapping details.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | number | Yes | The AniList ID (e.g. 21 for One Piece, 16498 for Attack on Titan S4) |
Example cURL:
curl -s "http://localhost:3000/api/anime/anilist/21"
Additional Response Attributes:
This endpoint automatically injects mapping metadata into the standard data object:
{
"success": true,
"data": {
"id": "6989b8a429cf95f4eb03b52f",
"title": {
"romaji": "One Piece",
"english": "One Piece",
"native": "ONE PIECE"
},
"anilist_id": 21,
"mapped_by": "anilist-to-animetsu-v1",
"match_score": 125
}
}
</details>
<details>
<summary><b>2. Home Feed <code>/api/home</code></b></summary>
Fetch the curated home feed containing spot-lighted animes, latest episodes, etc.
Example cURL:
curl -s "http://localhost:3000/api/home"
</details>
<details>
<summary><b>3. Popular Anime <code>/api/popular</code></b></summary>
Fetch the all-time popular animes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Page number (default: 1) |
Example cURL:
curl -s "http://localhost:3000/api/popular?page=1"
</details>
<details>
<summary><b>4. Recent Episodes <code>/api/recent</code></b></summary>
Fetch recently updated anime episodes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Page number (default: 1) |
Example cURL:
curl -s "http://localhost:3000/api/recent?page=1"
</details>
<details>
<summary><b>5. Trending Anime <code>/api/trending</code></b></summary>
Fetch currently trending animes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Page number (default: 1) |
Example cURL:
curl -s "http://localhost:3000/api/trending?page=1"
</details>
<details>
<summary><b>6. Top Rated Anime <code>/api/top-rated</code></b></summary>
Fetch top-rated animes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Page number (default: 1) |
Example cURL:
curl -s "http://localhost:3000/api/top-rated?page=1"
</details>
<details>
<summary><b>7. Upcoming Anime <code>/api/upcoming</code></b></summary>
Fetch animes scheduled for upcoming release.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Page number (default: 1) |
Example cURL:
curl -s "http://localhost:3000/api/upcoming?page=1"
</details>
<details>
<summary><b>8. Airing Schedule <code>/api/schedule</code></b></summary>
Fetch the airing schedule for current animes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| date | string | No | Date format YYYY-MM-DD |
Example cURL:
curl -s "http://localhost:3000/api/schedule"
</details>
<details>
<summary><b>9. Random Anime <code>/api/random</code></b></summary>
Fetch a random anime suggestion.
Example cURL:
curl -s "http://localhost:3000/api/random"
</details>
<details>
<summary><b>10. Seasonal Anime <code>/api/season</code></b></summary>
Fetch animes for a specific season and year.
Example cURL:
curl -s "http://localhost:3000/api/season"
</details>
<details>
<summary><b>11. Search Anime <code>/api/search</code></b></summary>
Search for animes by query.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| q | string | Yes | The search query |
Example cURL:
curl -s "http://localhost:3000/api/search?q=naruto"
Response Schema:
{
"success": true,
"data": {
"animes": [
{
"id": "12345",
"name": { "english": "Naruto", "romaji": "Naruto" },
"poster": "https://...",
"episodes": { "sub": 220, "dub": 220 }
}
]
}
}
</details>
<details>
<summary><b>12. Get Episodes <code>/api/anime/[id]/episodes</code></b></summary>
Fetch all episodes for a specific anime ID.
[!TIP] AniList Dynamic Option: You can also use AniList ID via
GET /api/anime/anilist/[id]/episodes(e.g./api/anime/anilist/21/episodes). It dynamically resolves the AniList ID and returns the exact same format.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | string | Yes | The Anime ID (or AniList ID if hitting /anilist/[id]/episodes) |
Example cURL:
curl -s "http://localhost:3000/api/anime/6989bf2429cf95f4eb040650/episodes"
# Or using AniList ID:
curl -s "http://localhost:3000/api/anime/anilist/21/episodes"
Response Schema:
{
"success": true,
"data": [
{
"ep_num": 1,
"name": "I'm Luffy! The Man Who's Gonna Be King of the Pirates!",
"is_filler": false
}
]
}
</details>
<details>
<summary><b>13. Get Servers <code>/api/anime/[id]/servers/[ep]</code></b></summary>
Fetch available streaming servers for a specific episode.
[!TIP] AniList Dynamic Option: You can also use AniList ID via
GET /api/anime/anilist/[id]/servers/[ep](e.g./api/anime/anilist/21/servers/1). It dynamically maps the AniList ID and checks latency / stream health in real-time.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | string | Yes | The Anime ID (or AniList ID if hitting /anilist/[id]/servers/[ep]) |
| ep | number | Yes | The Episode Number |
Example cURL:
curl -s "http://localhost:3000/api/anime/6989bf2429cf95f4eb040650/servers/1"
# Or using AniList ID:
curl -s "http://localhost:3000/api/anime/anilist/21/servers/1"
Response Schema:
{
"success": true,
"data": [
{
"id": "pahe",
"default": true,
"tip": "Multi-quality (1080p, 720p)",
"working": true,
"sources": 3,
"latency_ms": 120
}
]
}
</details>
<details>
<summary><b>14. Watch / Stream <code>/api/watch</code></b></summary>
Fetch proxy streaming URLs, subtitles, and metadata.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | string | Conditional | The native Anime ID (Required if anilistid is not provided) |
| anilistid | string | Conditional | The AniList ID (Required if id is not provided) |
| ep | number | Yes | The Episode Number |
| server | string | No | The Server ID (default: pahe) |
| source_type | string | No | sub or dub (default: sub) |
Example cURL:
curl -s "http://localhost:3000/api/watch?id=6989bf2429cf95f4eb040650&ep=1&server=pahe&source_type=sub"
# Or using AniList ID:
curl -s "http://localhost:3000/api/watch?anilistid=21&ep=1&server=pahe&source_type=sub"
Response Schema:
{
"success": true,
"data": {
"sources": [
{
"url": "https://upstream.m3u8",
"proxy_url": "http://localhost:3000/api/proxy/hls?url=...",
"isM3U8": true
}
],
"subtitles": [
{
"url": "http://localhost:3000/api/proxy/hls?url=...",
"lang": "English",
"label": "English"
}
]
}
}
</details>
<details>
<summary><b>15. Best-Quality Download <code>/api/anime/[id]/download/[ep]</code></b></summary>
Fetch the best-quality HLS stream url along with a suggested .mp4 filename and a ready-to-paste ffmpeg copy-mux command.
[!TIP] AniList Dynamic Option: You can also use AniList ID via
GET /api/anime/anilist/[id]/download/[ep](e.g./api/anime/anilist/21/download/1). It dynamically maps the AniList ID.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | string | Yes | The native Anime ID (or numeric AniList ID) |
| ep | number | Yes | The Episode Number |
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| quality | string | No | Quality preference (e.g. 1080p, 720p, 480p) |
Example cURL:
curl -s "http://localhost:3000/api/anime/6989bf2429cf95f4eb040650/download/1?quality=1080p"
Response Schema:
{
"success": true,
"data": {
"id": "6989bf2429cf95f4eb040650",
"episode": "1",
"server": "pahe",
"quality": "1080p",
"type": "video/mpegurl",
"url": "https://...",
"proxy_url": "http://localhost:3000/api/proxy/hls?url=...",
"filename": "6989bf2429cf95f4eb040650_ep1_1080p_sub.mp4",
"subtitles": [],
"hint": "HLS stream — mux to MP4 with: ffmpeg -i \"http://localhost:3000/api/proxy/hls?url=...\" -c copy 6989bf2429cf95f4eb040650_ep1_1080p_sub.mp4"
}
}
</details>
<details>
<summary><b>16. Real Download Releases <code>/api/anime/[id]/downloads/[ep]</code></b></summary>
Get actual fansub download releases grouped by release group and quality.
[!TIP] AniList Dynamic Option: You can also use AniList ID via
GET /api/anime/anilist/[id]/downloads/[ep](e.g./api/anime/anilist/21/downloads/1). It dynamically maps the AniList ID.
| Path Variable | Type | Required | Description |
|---------------|------|----------|-------------|
| id | string | Yes | The native Anime ID (or numeric AniList ID) |
| ep | number | Yes | The Episode Number |
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| source | string | No | pahe (default: small per-episode direct DDL MP4s) or tosho (P2P magnets and direct download mirrors) |
| quality | string | No | Filter by quality (e.g. 1080p, 720p) |
| group | string | No | Filter by release group (e.g. SubsPlease) |
| type | string | No | Filter by translation type (sub, dub, raw) |
| limit | number | No | Limit the number of releases in the flat list |
Example cURL:
curl -s "http://localhost:3000/api/anime/6989bf2429cf95f4eb040650/downloads/1?source=pahe"
Response Schema:
{
"success": true,
"data": {
"id": "6989bf2429cf95f4eb040650",
"episode": "1",
"title": "One Piece",
"source": "animetosho",
"query": "One Piece 01",
"groups": [
{
"group": "SubsPlease",
"type": "sub",
"qualities": ["1080p", "720p", "480p"],
"releases": [
{
"title": "[SubsPlease] One Piece - 01 (1080p).mkv",
"group": "SubsPlease",
"quality": "1080p",
"container": "mkv",
"type": "sub",
"language": "English Sub",
"size_bytes": 1395864371,
"size_human": "1.3 GB",
"seeders": 153,
"leechers": 4,
"p2p_url": "https://storage.animetosho.org/p2p/...",
"magnet_uri": "magnet:?xt=urn:btih:...",
"nzb_url": "https://storage.animetosho.org/nzbs/...",
"view_page": "https://animetosho.org/view/...",
"nyaa_url": "https://nyaa.si/view/1799935",
"published_at": "2024-04-07T02:01:00Z",
"info_hash": "d2321a12ac71730eef9bca99435c8abe3ab7453e"
}
]
}
],
"flat": [ /* every release, sorted quality desc → seeders desc */ ]
}
}
</details>
💖 Credits
A huge thank you to Aniflix.in for the original custom player design and inspiration!
