Anime API
Anime API
8,500+ anime titles with HLS streaming, AniList metadata, season filtering, and signed CDN URLs — same self-hosted infrastructure as the Drama API.
Base URL
Anime API Base
https://api.splay.id
All anime endpoints are at /api/anime/* — consistent with /api/dramas/*.
Authentication
Same API key as the Drama API. Pass your key in the Authorization header:
Request Header
Copy
Authorization: Bearer sk_live_your_api_key_here
Quick Example
cURL — List Popular Anime
Copy
curl https://api.splay.id/api/anime/popular?per_page=10 \
-H"Authorization: Bearer sk_live_..."
# Response
{
"data": [
{
"id":"attack-on-titan",
"name":"Attack on Titan",
"cover_url":"https://cdn.splay.id/AnimeAssets/.../Poster.webp",
"genres": ["Action","Drama","Fantasy"],
"available_episodes": 87,
"anilist_data": {
"title": {"english":"Attack on Titan","romaji":"Shingeki no Kyojin"},
"averageScore": 84,
"popularity": 495000,
"status":"FINISHED",
"format":"TV",
"countryOfOrigin":"JP"
}
}
],
"meta": {"page": 1,"per_page": 10,"total": 8589,"total_pages": 859 }
}
Data Model
AnimeItem — List Response
Copy
{
"id":"attack-on-titan", // string — unique slug ID
"name":"Attack on Titan", // string — canonical name
"cover_url":"https://...", // string | null — CDN poster URL
"description":"...", // string | null
"genres": ["Action","Drama"], // string[]
"anilist_id": 16498, // number | null
"available_episodes": 87, // number — episodes with video
"updated_at":"2026-01-10T...", // string | null — ISO 8601
"anilist_data": {
"title": {
"romaji":"Shingeki no Kyojin",
"english":"Attack on Titan",
"native":"進撃の巨人"
},
"averageScore": 84, // 0–100 (divide by 10 for /10)
"popularity": 495000,
"status":"FINISHED", // RELEASING | FINISHED | NOT_YET_RELEASED | CANCELLED
"season":"SPRING", // WINTER | SPRING | SUMMER | FALL | null
"seasonYear": 2013,
"format":"TV", // TV | MOVIE | OVA | ONA | SPECIAL | MUSIC
"countryOfOrigin":"JP"// JP | CN | KR
}
}
EpisodeDetail — Single Episode
Copy
{
"id":"...",
"anime_id":"attack-on-titan",
"episode_number": 1,
"episode_title":"To You, in 2000 Years",
"video_urls": {
"1080p":"https://cdn.splay.id/...?sig=...&exp=...",
"720p":"https://cdn.splay.id/...?sig=...&exp=...",
"hls":"https://cdn.splay.id/.../playlist.m3u8?sig=...&exp=..."
},
"video_type":"hls", //"hls"|"mp4"
"subtitles": [
{
"lang":"id",
"label":"Indonesian",
"url":"https://cdn.splay.id/.../sub.id.vtt?sig=...&exp=...",
"is_default": true
},
{
"lang":"en",
"label":"English",
"url":"https://cdn.splay.id/.../sub.en.vtt?sig=...&exp=...",
"is_default": false
}
],
"url_expires_at":"2026-03-09T11:00:00Z"// signed URL TTL
}
Filtering & Sorting
Parameter Type Values Description
genre string Action, Romance, … Filter by genre name (exact match)
origin string JP / CN / KR — or all / jp / cn / other for /latest-update Filter by country of origin. Exact uppercase (JP/CN) on most endpoints; lowercase shorthand (jp/cn/other) on /latest-update
status string RELEASING / FINISHED / NOT_YET_RELEASED Filter by airing status
season string WINTER / SPRING / SUMMER / FALL Filter by airing season
year integer e.g. 2024 Filter by season year
sort_by string popularity / rating / latest Sort order (default: popularity)
page integer ≥ 1 Page number (default: 1)
per_page integer 1–100 Items per page (default: 20) Anime Endpoints
Anime Endpoints
Complete reference for all 11 anime API endpoints. All require Authorization: Bearer header when REQUIRE_API_KEY is enabled.
All Endpoints
Method Endpoint Description
GET /api/anime List anime (paginated, filter by genre/origin/status/season/year, sort by popularity/rating/latest)
GET /api/anime/popular Popular anime ranked by AniList popularity score
GET /api/anime/latest-update Curated recently-updated list (category curation, filter: origin=all/jp/cn/other)
GET /api/anime/season Seasonal anime (filter by season: WINTER/SPRING/SUMMER/FALL + year)
GET /api/anime/upcoming Upcoming / announced anime (status: NOT_YET_RELEASED)
GET /api/anime/genres List all available anime genres
GET /api/anime/spotlight Spotlight / featured anime (high score + popularity)
GET /api/anime/search Full-text search anime by title (query param: q)
GET /api/anime/:id Anime detail with full AniList metadata and episode count
GET /api/anime/:id/episodes Paginated episode list with subtitle info and thumbnails
GET /api/anime/:id/episodes/:ep Single episode — signed HLS/MP4 video URLs + subtitle tracks
Query Parameters
Parameter Type Default
page integer 1
per_page integer 20
genre string —
origin string —
status string —
season string —
year integer —
sort_by string popularity
q string —
expires_in integer 1800
Response Shapes
GET /api/anime/latest-update — Curated Recently Updated
Copy
# All origins (default)
GET /api/anime/latest-update?page=1&per_page=30
# Japanese anime only
GET /api/anime/latest-update?origin=jp
# Chinese donghua only
GET /api/anime/latest-update?origin=cn
# Other origins (KR, TH, etc.)
GET /api/anime/latest-update?origin=other
# Response — same shape as /api/anime list
{
"data": [
{
"id":"bungou-stray-dogs",
"name":"Bungou Stray Dogs",
"cover_url":"https://...",
"available_episodes": 60,
"anilist_data": {
"countryOfOrigin":"JP",
"status":"RELEASING",
"averageScore": 80
},
"updated_at":"2026-03-12T04:00:00Z"
}
],
"meta": {"page": 1,"per_page": 30,"total": 312,"total_pages": 11 }
}
GET /api/anime — Paginated List
Copy
{
"data": [
{
"id":"attack-on-titan",
"name":"Attack on Titan",
"cover_url":"https://cdn.splay.id/AnimeAssets/Attack_on_Titan/Poster.webp",
"description":"...",
"genres": ["Action","Mystery","Drama"],
"anilist_id": 16498,
"available_episodes": 87,
"updated_at":"2026-01-10T08:00:00Z",
"anilist_data": {
"title": {"romaji":"Shingeki no Kyojin","english":"Attack on Titan","native":"進撃の巨人"},
"averageScore": 84,
"popularity": 495000,
"status":"FINISHED",
"season":"SPRING",
"seasonYear": 2013,
"format":"TV",
"countryOfOrigin":"JP"
}
}
],
"meta": {"page": 1,"per_page": 20,"total": 8589,"total_pages": 430 }
}
GET /api/anime/:id — Detail
Copy
{
"data": {
"anime": {
"id":"attack-on-titan",
"name":"Attack on Titan",
"cover_url":"https://...",
"description":"...",
"genres": ["Action","Drama"],
"anilist_id": 16498,
"available_episodes": 87,
"anilist_data": { /* full AniList object */ }
},
"episode_count": 87
}
}
GET /api/anime/:id/episodes — Episode List
Copy
{
"data": [
{
"id":"...",
"anime_id":"attack-on-titan",
"episode_number": 1,
"episode_title":"To You, in 2000 Years",
"has_subtitle": true,
"subtitle_langs": ["id","en"],
"thumbnail":"https://cdn.splay.id/.../thumb.jpg"
}
],
"meta": {"page": 1,"per_page": 50,"total": 87,"total_pages": 2 }
}
GET /api/anime/:id/episodes/:ep — Single Episode
Copy
{
"data": {
"id":"...",
"anime_id":"attack-on-titan",
"episode_number": 1,
"episode_title":"To You, in 2000 Years",
"video_urls": {
"hls":"https://cdn.splay.id/.../playlist.m3u8?sig=HMAC&exp=1741521600",
"1080p":"https://cdn.splay.id/.../1080p.mp4?sig=HMAC&exp=1741521600",
"720p":"https://cdn.splay.id/.../720p.mp4?sig=HMAC&exp=1741521600",
"480p":"https://cdn.splay.id/.../480p.mp4?sig=HMAC&exp=1741521600"
},
"video_type":"hls",
"subtitles": [
{"lang":"id","label":"Indonesian","url":"https://cdn.splay.id/.../sub.id.vtt?sig=HMAC&exp=...","is_default": true },
{"lang":"en","label":"English","url":"https://cdn.splay.id/.../sub.en.vtt?sig=HMAC&exp=...","is_default": false }
],
"url_expires_at":"2026-03-09T12:00:00Z"
}
}
Previous Streaming
Streaming & Signed URLs
Anime video is delivered via pre-signed CDN URLs with configurable TTL. Supports HLS adaptive streaming, multi-quality direct MP4, and VTT subtitle tracks.
Delivery Architecture
Storage
Cloudflare R2 (anime-project bucket)
CDN
cdn.splay.id (Hono CF Worker)
Signing
HMAC-SHA256 — Rust API signs, Worker verifies
Default TTL
1800s (30 min) · max 14400s (4h)
HLS Streaming
Most anime episodes are served as HLS adaptive streams. Use the hls key from video_urls as the source for your player.
HLS Playback — video.js
Copy
// The signed .m3u8 URL is ready to use directly
const hlsUrl = episode.video_urls["hls"];
// video.js example
const player = videojs("my-player");
player.src({ src: hlsUrl, type:"application/x-mpegURL"});
// hls.js example
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(hlsUrl);
hls.attachMedia(videoElement);
}
HLS Playback — HTML5 native (Safari)
Copy
Multi-Quality Direct MP4
When direct MP4 variants exist, they appear alongside hls in video_urls. Keys vary by available quality.
Quality Selection
Copy
const { video_urls } = episode;
// Preferred quality order
function pickUrl(urls) {
return (
urls["hls"] || // HLS adaptive — best for streaming
urls["1080p"] ||
urls["720p"] ||
urls["480p"] ||
Object.values(urls)[0] ||""
);
}
// Quality switcher — build a menu from available keys
const qualities = Object.keys(video_urls).filter(k => k !=="hls");
// e.g. ["1080p","720p","480p"]
Subtitle Tracks
Subtitle URLs are pre-signed VTT files. Attach them as elements or load with your player's subtitle API.
HTML5 Video with Subtitles
Copy
{episode.subtitles.map(sub => (
))}
video.js Subtitle Integration
Copy
player.ready(() => {
subtitles.forEach(sub => {
player.addRemoteTextTrack({
kind:"subtitles",
src: sub.url,
srclang: sub.lang,
label: sub.label,
default: sub.is_default,
}, false);
});
});
Signed URL TTL
All video and subtitle URLs are signed with HMAC-SHA256 and expire after a configurable TTL. Pass expires_in to the episode endpoint to extend TTL for long-playing sessions.
URLs expire after the configured TTL. If your user pauses for longer than the TTL, re-fetch the episode endpoint to get fresh URLs. Default: 1800s (30 min). Max: 14400s (4h).
Custom TTL — 4 hour session
Copy
GET /api/anime/attack-on-titan/episodes/1?expires_in=14400
Authorization: Bearer sk_live_...
# Response includes URL with 4-hour expiry
{
"data": {
"video_urls": {
"hls":"https://cdn.splay.id/.../playlist.m3u8?exp=1741528800&sig=..."
},
"url_expires_at":"2026-03-09T16:00:00Z"// 4h from now
}
}
R2 Storage Structure
Object Paths
Copy
# Video qualities
{anime_id}/{episode_number}/1080p.mp4
{anime_id}/{episode_number}/720p.mp4
{anime_id}/{episode_number}/480p.mp4
{anime_id}/{episode_number}/playlist.m3u8 # HLS master playlist
# Subtitle tracks
{anime_id}/{episode_number}/sub.{lang}.vtt # e.g. sub.id.vtt, sub.en.vtt
# Cover images
{anime_id}/cover.webp
Previous Quality Plans
Anime Quality Plans
Video quality returned by /anime/:id/episodes/:ep is filtered per API plan. The API only returns signed URLs for qualities your plan allows — unauthenticated /play viewers follow the Free tier.
Plan Overview
Plan Max Quality HLS / Auto Direct MP4
Free 480p ✗ 360p, 480p
Basic 480p ✗ 360p, 480p
Advanced 720p ✗ 360p – 720p
Pro 1080p ✗ 360p – 1080p
Enterprise HLS (Auto) ✓ All
Custom HLS (Auto) ✓ All
Per-Plan Detail
Free
Max: 480p
Allowed
360p
480p
Denied
540p
720p
1080p
Auto (HLS)
Default for all unauthenticated requests and /play section visitors.
Basic
Max: 480p
Allowed
360p
480p
Denied
540p
720p
1080p
Auto (HLS)
Same quality cap as Free. Upgrade for higher rate limits.
Advanced
Max: 720p
Allowed
360p
480p
540p
720p
Denied
1080p
Auto (HLS)
HD streaming. Good for mobile and mid-range clients.
Pro
Max: 1080p
Allowed
360p
480p
540p
720p
1080p
Denied
Auto (HLS/Master)
Full HD direct MP4. HLS adaptive stream excluded — use 1080p direct.
Enterprise
Max: HLS (Auto)
Allowed
360p
480p
540p
720p
1080p
Auto (HLS)
Master
All qualities unlocked including ABR/HLS adaptive streaming — best for production apps.
Custom
Max: HLS (Auto)
Allowed
360p
480p
540p
720p
1080p
Auto (HLS)
Master
Same as Enterprise. Custom rate limits and pricing.
How It Works
When you call GET /api/anime/:id/episodes/:ep, the API reads your plan from the API key, filters video_urls to only include allowed qualities, then signs each URL with your plan's max quality embedded in the HMAC.
The CDN worker (cdn.splay.id) re-verifies the quality claim on every request — even if a client modifies the URL parameters, the HMAC won't match and the request will be rejected with 403 Forbidden.
Enterprise only: HLS adaptive streaming (hls / master keys) is exclusively available to Enterprise and Custom plan API keys. All lower plans receive only direct MP4 URLs up to their quality cap.
Response Comparison
Free / Basic
video_urls
{
"480p":"https://cdn-anime..."
}
Advanced
video_urls
{
"480p":"https://cdn-anime...",
"720p":"https://cdn-anime..."
}
Pro
video_urls
{
"480p":"https://cdn-anime...",
"720p":"https://cdn-anime...",
"1080p":"https://cdn-anime..."
}
Enterprise
video_urls
{
"480p":"https://cdn-anime...",
"720p":"https://cdn-anime...",
"1080p":"https://cdn-anime...",
"hls":"https://cdn-anime.../playlist.m3u8"
} Anime API
Code Examples
Ready-to-use examples for the Anime API. Paste your API key below to try live requests and inject it into code snippets.
Your API Key
Active
Paste your API key to authenticate the "Try" buttons and inject it into code examples.Get your key →
sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc
Save
Clear
Try It Live
Homepage
GET
/api/anime/home
Send
GET
/api/anime/home?origin=jp
Send
GET
/api/anime/home?origin=cn
Send
Browse Anime
GET
/api/anime?page=1&per_page=5
Send
GET
/api/anime?genre=Action&per_page=5
Send
GET
/api/anime?origin=JP&status=RELEASING&per_page=5
Send
GET
/api/anime?season=WINTER&year=2024&per_page=5
Send
GET
/api/anime?sort_by=rating&per_page=5
Send
GET
/api/anime/popular?per_page=5
Send
GET
/api/anime/popular?origin=CN&per_page=5
Send
GET
/api/anime/latest-update?per_page=5
Send
GET
/api/anime/latest-update?origin=cn&per_page=5
Send
GET
/api/anime/latest-update?origin=jp&per_page=5
Send
GET
/api/anime/latest-update?origin=other&per_page=5
Send
GET
/api/anime/genres
Send
Detail & Episodes
GET
/api/anime/attack-on-titan
Send
GET
/api/anime/attack-on-titan/episodes?page=1&per_page=10
Send
GET
/api/anime/attack-on-titan/episodes/1
Send
GET
/api/anime/attack-on-titan/episodes/1?expires_in=14400
Send
Search
GET
/api/anime/search?q=attack+on+titan
Send
GET
/api/anime/search?q=romance&genre=Romance&per_page=5
Send
cURL
cURL Examples
Copy
API_KEY="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc"
BASE="https://api.splay.id"
# ── List anime ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime?per_page=20"
# ── Filter: genre, origin, status, season/year ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime?genre=Action&origin=JP&per_page=20"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime?season=WINTER&year=2024&sort_by=rating&per_page=20"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime?status=RELEASING&per_page=20"
# ── Home (combined first-load: spotlight + latest_update + popular) ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/home"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/home?origin=jp"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/home?origin=cn"
# ── Popular & Latest Update ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/popular?per_page=10"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/popular?origin=CN&per_page=10"# donghua only
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/latest-update?per_page=30"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/latest-update?origin=jp&per_page=30"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/latest-update?origin=cn&per_page=30"
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/latest-update?origin=other&per_page=30"
# ── Genres list ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/genres"
# ── Search ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/search?q=attack+on+titan"
# ── Anime detail ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/attack-on-titan"
# ── Episode list ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/attack-on-titan/episodes?page=1&per_page=50"
# ── Single episode (signed video + subtitle URLs, 1h TTL) ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/attack-on-titan/episodes/1"
# ── Custom TTL (4h) ──
curl -H"Authorization: Bearer $API_KEY""$BASE/api/anime/attack-on-titan/episodes/1?expires_in=14400"
JavaScript / TypeScript
Browse & Filter
Copy
const BASE ="https://api.splay.id";
const API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc";
const headers = { Authorization: `Bearer ${API_KEY}` };
// ── Home — spotlight + latest_update (30) + popular (24) in one request ──
const { data: home } = await (await fetch(`${BASE}/api/anime/home`, { headers })).json();
const { data: homeJP } = await (await fetch(`${BASE}/api/anime/home?origin=jp`, { headers })).json();
console.log(`${home.spotlight.length} spotlight, ${home.latest_update.length} latest, ${home.popular.length} popular`);
// ── List anime ──
const res = await fetch(`${BASE}/api/anime?per_page=20`, { headers });
const { data, meta } = await res.json();
console.log(`${meta.total} anime`);
data.forEach(a => {
const score = a.anilist_data?.averageScore;
console.log(`[${a.id}] ${a.name} — ${score ??"N/A"}/100, ${a.available_episodes} eps`);
});
// ── Filter: genre, origin, status ──
const action = await fetch(
`${BASE}/api/anime?genre=Action&origin=JP&status=RELEASING&per_page=20`,
{ headers }
);
const winter2024 = await fetch(
`${BASE}/api/anime?season=WINTER&year=2024&sort_by=rating&per_page=20`,
{ headers }
);
// ── Popular ──
const { data: popular } = await (await fetch(`${BASE}/api/anime/popular?per_page=10`, { headers })).json();
const { data: donghua } = await (await fetch(`${BASE}/api/anime/popular?origin=CN&per_page=10`, { headers })).json();
// ── Latest Update — curated recently-updated (origin: all | jp | cn | other) ──
const { data: latestAll } = await (await fetch(`${BASE}/api/anime/latest-update?per_page=30`, { headers })).json();
const { data: latestJP } = await (await fetch(`${BASE}/api/anime/latest-update?origin=jp&per_page=30`, { headers })).json();
const { data: latestCN } = await (await fetch(`${BASE}/api/anime/latest-update?origin=cn&per_page=30`, { headers })).json();
// ── Genres ──
const { data: genres } = await (await fetch(`${BASE}/api/anime/genres`, { headers })).json();
// ── Search ──
const { data: results } = await (await fetch(`${BASE}/api/anime/search?q=attack+on+titan`, { headers })).json();
Stream an Episode
Copy
const BASE ="https://api.splay.id";
const API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc";
const headers = { Authorization: `Bearer ${API_KEY}` };
// ── Anime detail ──
const { data: { anime, episode_count } } = await (
await fetch(`${BASE}/api/anime/attack-on-titan`, { headers })
).json();
console.log(`${anime.name} — ${episode_count} eps | score: ${anime.anilist_data?.averageScore}`);
// ── Episode list ──
const { data: episodes, meta } = await (
await fetch(`${BASE}/api/anime/attack-on-titan/episodes?page=1&per_page=50`, { headers })
).json();
console.log(`${meta.total} total episodes`);
// ── Single episode — request 4h TTL so URL stays valid during playback ──
const { data: ep } = await (
await fetch(`${BASE}/api/anime/attack-on-titan/episodes/1?expires_in=14400`, { headers })
).json();
console.log("Expires:", ep.url_expires_at);
// ep.video_urls: { hls,"1080p","720p","480p"}
// ep.subtitles: [{ lang, label, url, is_default }]
// ── Play with HLS.js ──
import Hls from"hls.js";
const video = document.querySelector("video");
if (Hls.isSupported() && ep.video_urls.hls) {
const hls = new Hls();
hls.loadSource(ep.video_urls.hls);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, () => video.play());
} else {
video.src = ep.video_urls["1080p"] ?? ep.video_urls["720p"];
video.play();
}
// ── Load subtitles ──
ep.subtitles.forEach(sub => {
const track = document.createElement("track");
track.kind ="subtitles";
track.label = sub.label;
track.srclang = sub.lang;
track.src = sub.url;
if (sub.is_default) track.default = true;
video.appendChild(track);
});
Python
Full Example
Copy
import requests
BASE ="https://api.splay.id"
API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc"
headers = {"Authorization": f"Bearer {API_KEY}"}
# ── List anime ──
res = requests.get(f"{BASE}/api/anime", params={"per_page": 20}, headers=headers)
for a in res.json()["data"]:
score = (a.get("anilist_data") or {}).get("averageScore","N/A")
print(f"[{a['id']}] {a['name']} — {score}/100, {a['available_episodes']} eps")
# ── Filter ──
requests.get(f"{BASE}/api/anime", params={
"genre":"Action","origin":"JP","status":"RELEASING","per_page": 20
}, headers=headers)
requests.get(f"{BASE}/api/anime", params={
"season":"WINTER","year": 2024,"sort_by":"rating","per_page": 20
}, headers=headers)
# ── Popular, Genres, Search ──
popular = requests.get(f"{BASE}/api/anime/popular", params={"per_page": 10}, headers=headers)
donghua = requests.get(f"{BASE}/api/anime/popular", params={"origin":"CN","per_page": 10}, headers=headers)
genres = requests.get(f"{BASE}/api/anime/genres", headers=headers)
search = requests.get(f"{BASE}/api/anime/search", params={"q":"attack on titan"}, headers=headers)
# ── Latest Update — curated recently-updated list ──
# origin:"all"(default) |"jp"|"cn"|"other"
latest_all = requests.get(f"{BASE}/api/anime/latest-update", params={"per_page": 30}, headers=headers)
latest_jp = requests.get(f"{BASE}/api/anime/latest-update", params={"origin":"jp","per_page": 30}, headers=headers)
latest_cn = requests.get(f"{BASE}/api/anime/latest-update", params={"origin":"cn","per_page": 30}, headers=headers)
for a in latest_cn.json()["data"]:
print(f"[Donghua] {a['name']} — {a['available_episodes']} eps")
# ── Anime detail ──
detail = requests.get(f"{BASE}/api/anime/attack-on-titan", headers=headers)
info = detail.json()["data"]
print(f"{info['anime']['name']} — {info['episode_count']} episodes")
# ── Episode list ──
eps = requests.get(f"{BASE}/api/anime/attack-on-titan/episodes", params={
"page": 1,"per_page": 50
}, headers=headers)
for ep in eps.json()["data"]:
langs =",".join(ep.get("subtitle_langs", [])) or"none"
print(f"Ep {ep['episode_number']}: {ep.get('episode_title', '')} (subs: {langs})")
# ── Single episode (signed URLs, 4h TTL) ──
ep = requests.get(
f"{BASE}/api/anime/attack-on-titan/episodes/1",
params={"expires_in": 14400}, headers=headers
).json()["data"]
print("Expires:", ep["url_expires_at"])
video_urls = ep.get("video_urls", {})
print("HLS:", video_urls.get("hls"))
print("1080p:", video_urls.get("1080p"))
for sub in ep.get("subtitles", []):
mark ="[default]"if sub.get("is_default") else""
print(f"[{sub['lang']}] {sub['label']}{mark}: {sub['url']}") Baca API
Baca Overview
Manga, manhwa, and manhua reading API. Three categories, chapter-based pagination, per-chapter signed image URLs. Same familiar data model used across the platform -- titles, tags, and chapters instead of episodes.
Categories
3
Titles
Growing
Chapters
Growing
Format
Image
Base URL
Base URL
https://api.splay.id
GET
/api/baca/{category}?per_page=20
Categories
All Baca endpoints are scoped by category. Replace {category} with one of the following:
manga
Japanese comics -- read right-to-left
manhwa
Korean comics -- vertical scroll format
manhua
Chinese comics -- vertical scroll format
Data Model
Baca uses the same Drama type as other APIs, with chapters instead of episodes. Each title has a cover image, tags, introduction, and chapter list. Each chapter provides a signed directory URL -- append image filenames to construct per-page image URLs.
Field Type Description
id number Unique title ID
title string Title name
cover_url string Signed cover image URL
provider_slug string Provider identifier
chapter_count number Total chapter count
introduction string Synopsis / description
tags Tag[] Genre/category tags
chapters Chapter[] Chapter list with signed image URLs
Chapter Format (Images)
Each chapter contains individual images (JPEG/PNG, one per page). The content_url field holds a signed chapter directory URL. The qualities object contains the page count and format. Construct image URLs by appending the page filename to the directory URL.
Field Type Description
content_url string Signed chapter directory URL (HMAC-SHA256, 30min TTL)
qualities.pages number Total number of images (pages) in the chapter
qualities.format string Image format -- "image" (original JPEG/PNG)
Image URL pattern: {content_url}/001.jpg?sig=...&expires=... -- one signature covers all images in the chapter (the sig and expires params from content_url are reused). All URLs are HMAC-SHA256 signed with 30-minute default TTL (max 4 hours via expires_in). No subtitle support -- manga content is visual.
Authentication
Same API Key
Baca endpoints use the same Authorization: Bearer authentication as all other APIs. Get your key atapi-dashboard.
Quick Example
GET /api/baca/manga
Copy
curl"https://api.splay.id/api/baca/manga?per_page=20"\
-H"Authorization: Bearer " Baca API
Endpoints
Dedicated /api/baca/{category}/* endpoints for manga, manhwa, and manhua content. All endpoints follow the same patterns as other APIs -- pagination, sorting, filtering, and per-chapter signed image URLs.
Authentication Required
All data endpoints require Authorization: Bearer . Get your key atapi-dashboard.
Category Path Parameter
Replace {category} with manga, manhwa, or manhua in all endpoints below.
GET
/api/baca/{category}
List titles in a category -- paginated, with filtering and sorting. Category must be manga, manhwa, or manhua.
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
provider string optional Filter by provider slug
tag string optional Filter by tag name
language string optional ISO 639-1 language code (en, ja, ko, zh, ...)
sort_by string optional updated_at | created_at | title | play_count | chapter_count
sort_order string optional desc | asc
Example
/api/baca/manga?per_page=20&sort_by=updated_at
GET
/api/baca/{category}/popular
Popular titles ranked by read count
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
language string optional ISO 639-1 language code
Example
/api/baca/manhwa/popular?per_page=10
GET
/api/baca/{category}/trending
Trending titles (recently updated, sorted by read count)
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
language string optional ISO 639-1 language code
Example
/api/baca/manhua/trending
GET
/api/baca/{category}/search
Full-text search across titles in a category
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
q string required Search query
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
Example
/api/baca/manga/search?q=one+piece
GET
/api/baca/{category}/alphabet
Browse A-Z -- returns letter counts, or titles starting with a specific letter
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
letter string optional Single letter (A-Z) to filter titles. Omit for letter counts.
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
Example
/api/baca/manga/alphabet?letter=N
GET
/api/baca/{category}/{id}
Get title detail with tags and chapters with signed image URLs
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
id number required Title ID (integer)
expires_in number optional Signed URL TTL in seconds (max 14400, default 1800)
Example
/api/baca/manga/42
GET
/api/baca/{category}/{id}/chapters
Paginated chapters for a title with signed image URLs
Parameter Type Required Description
category string required manga | manhwa | manhua (path param)
id number required Title ID (integer)
page number optional Page number (default: 1)
per_page number optional Results per page (max 100, default 20)
expires_in number optional Signed URL TTL in seconds (max 14400, default 1800)
Example
/api/baca/manga/42/chapters?page=1&per_page=50
Chapter Response Shape
Each chapter object contains a content_url (signed chapter directory path) and a qualities object with page count and format. Construct individual image URLs by appending the page filename to the directory URL.
Chapter object
Copy
{
"id": 456,
"drama_id": 78,
"episode_index": 1,
"episode_name":"Chapter 1",
"status":"published",
// Signed chapter directory URL (HMAC-SHA256, 30min TTL)
"content_url":"https://cdn.splay.id/baca/manga/42/1?sig=abc123&expires=1742320000",
// Image metadata -- page count + format
"qualities": {
"pages": 24,
"format":"image"
},
// No subtitles for manga content
"subtitles": [],
"released_at":"2025-08-01T00:00:00Z",
"created_at":"2026-02-20T08:00:00Z"
}
// Construct image URLs:
// {content_url}/001.jpg?sig=abc123&expires=1742320000
// {content_url}/002.jpg?sig=abc123&expires=1742320000
// ... up to {qualities.pages} images
// The sig and expires params from content_url apply to all images in the chapter.
Pagination
All list endpoints return paginated responses with a meta object containing pagination info.
Response meta
Copy
{
"data": [...],
"meta": {
"page": 1,
"per_page": 20,
"total": 500,
"total_pages": 25
}
} Baca API
Code Examples
Ready-to-use examples for the Baca API -- browse manga, manhwa, manhua, search titles, and read chapters via signed image URLs.
Your API Key
Active
Paste your API key to authenticate the "Try" buttons and inject it into code examples.Get your key →
sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc
Save
Clear
Try It Live
Browse Manga
GET
/api/baca/manga?per_page=5
Send
GET
/api/baca/manga?sort_by=updated_at&sort_order=desc&per_page=5
Send
GET
/api/baca/manga/popular?per_page=5
Send
GET
/api/baca/manga/trending?per_page=5
Send
GET
/api/baca/manga/alphabet
Send
GET
/api/baca/manga/alphabet?letter=N&per_page=5
Send
Browse Manhwa
GET
/api/baca/manhwa?per_page=5
Send
GET
/api/baca/manhwa/popular?per_page=5
Send
Browse Manhua
GET
/api/baca/manhua?per_page=5
Send
GET
/api/baca/manhua/popular?per_page=5
Send
Search
GET
/api/baca/manga/search?q=one+piece&per_page=5
Send
GET
/api/baca/manhwa/search?q=solo+leveling&per_page=5
Send
cURL
cURL Examples
Copy
API_KEY="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc"
BASE="https://api.splay.id"
# -- List manga --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manga?per_page=20"
# -- Sort by latest updated --
curl -H"Authorization: Bearer $API_KEY"\
"$BASE/api/baca/manga?sort_by=updated_at&sort_order=desc&per_page=20"
# -- Popular manhwa --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manhwa/popular"
# -- Trending manhua --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manhua/trending"
# -- Browse A-Z (get letter counts) --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manga/alphabet"
# -- Browse letter N --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manga/alphabet?letter=N"
# -- Search --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manga/search?q=one+piece"
# -- Title detail (all chapters + signed image URLs) --
curl -H"Authorization: Bearer $API_KEY""$BASE/api/baca/manga/TITLE_ID"
# -- Paginated chapters --
curl -H"Authorization: Bearer $API_KEY"\
"$BASE/api/baca/manga/TITLE_ID/chapters?page=1&per_page=50"
# -- Construct image URLs from chapter response --
# content_url = signed chapter directory URL
# Append page filename: {content_url}/001.jpg?sig=...&expires=...
# The sig and expires from content_url are reused for all images
JavaScript / TypeScript
Browse & Search
Copy
const BASE ="https://api.splay.id";
const API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc";
const headers = { Authorization: `Bearer ${API_KEY}` };
// -- List manga (paginated) --
const res = await fetch(`${BASE}/api/baca/manga?per_page=20`, { headers });
const { data, meta } = await res.json();
console.log(`${meta.total} titles (page ${meta.page}/${meta.total_pages})`);
data.forEach(d => console.log(`[${d.id}] ${d.title}`));
// -- Popular manhwa --
const { data: popular } = await (
await fetch(`${BASE}/api/baca/manhwa/popular`, { headers })
).json();
// -- Trending manhua --
const { data: trending } = await (
await fetch(`${BASE}/api/baca/manhua/trending`, { headers })
).json();
// -- Search --
const { data: results, meta: searchMeta } = await (
await fetch(`${BASE}/api/baca/manga/search?q=one+piece`, { headers })
).json();
console.log(`Found ${searchMeta.total} results`);
// -- A-Z Browse --
const { data: letters } = await (
await fetch(`${BASE}/api/baca/manga/alphabet`, { headers })
).json();
// letters = [{"letter":"A","count": 42 }, ...]
Get Detail + Render Chapter Images
Copy
const BASE ="https://api.splay.id";
const API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc";
const headers = { Authorization: `Bearer ${API_KEY}` };
// -- Title detail (title + chapters with signed URLs) --
const res = await fetch(`${BASE}/api/baca/manga/TITLE_ID`, { headers });
const { data: { drama, chapters } } = await res.json();
console.log(`${drama.title} -- ${chapters.length} chapters`);
// -- Chapter image URLs --
const chapter = chapters[0];
// content_url = signed chapter directory (HMAC-SHA256, 30min TTL)
const chapterUrl = chapter.content_url;
const pageCount = chapter.qualities?.pages;
const format = chapter.qualities?.format; // "image"
console.log(`Chapter 1: ${pageCount} pages (${format})`);
// -- Extract sig and expires from the directory URL --
const url = new URL(chapterUrl);
const sig = url.searchParams.get("sig");
const expires = url.searchParams.get("expires");
// -- Construct individual page image URLs --
const imageUrls = [];
for (let i = 1; i <= pageCount; i++) {
const filename = String(i).padStart(3, "0") + ".jpg";
imageUrls.push(`${chapterUrl}/${filename}?sig=${sig}&expires=${expires}`);
}
// -- Render images in a container --
const container = document.getElementById("reader");
if (container) {
imageUrls.forEach((src, i) => {
const img = document.createElement("img");
img.src = src;
img.alt = `Page ${i + 1}`;
img.loading = i < 3 ? "eager" : "lazy"; // lazy-load after first 3
img.style.maxWidth = "100%";
container.appendChild(img);
});
}
// -- Paginated chapters --
const { data: chapters, meta } = await (
await fetch(`${BASE}/api/baca/manga/TITLE_ID/chapters?page=1&per_page=50`, { headers })
).json();
console.log(`${meta.total} total chapters, showing ${chapters.length}`);
Python
Full Example
Copy
import requests
BASE ="https://api.splay.id"
API_KEY ="sk_live_afb4c24ea8c44d30a06c67ecc67a8f85a46a0987c8d899bc"
headers = {"Authorization": f"Bearer {API_KEY}"}
# -- List manga --
res = requests.get(f"{BASE}/api/baca/manga", params={
"per_page": 20,"sort_by":"updated_at","sort_order":"desc"
}, headers=headers)
for d in res.json()["data"]:
print(f"[{d['id']}] {d['title']} ({d['chapter_count']} chapters)")
# -- Popular manhwa --
popular = requests.get(f"{BASE}/api/baca/manhwa/popular", headers=headers)
for d in popular.json()["data"][:5]:
print(f"Popular: {d['title']}")
# -- Search --
search = requests.get(f"{BASE}/api/baca/manga/search", params={
"q":"one piece","per_page": 10
}, headers=headers)
results = search.json()
print(f"Found {results['meta']['total']} results")
# -- Title detail with chapters --
detail = requests.get(f"{BASE}/api/baca/manga/TITLE_ID", headers=headers)
info = detail.json()["data"]
title_info, chapters = info["drama"], info["chapters"]
print(f"\n=== {title_info['title']} ===")
print(f"Chapters: {len(chapters)}")
# -- Image URLs (signed, 30min TTL) --
ch = chapters[0]
chapter_url = ch.get("content_url") # signed directory URL
qualities = ch.get("qualities") or {}
page_count = qualities.get("pages", 0)
img_format = qualities.get("format", "image")
print(f"\nChapter 1: {page_count} pages ({img_format})")
# -- Extract sig and expires from the directory URL --
from urllib.parse import urlparse, parse_qs
parsed = urlparse(chapter_url)
params = parse_qs(parsed.query)
sig = params.get("sig", [""])[0]
expires = params.get("expires", [""])[0]
# -- Download all page images --
import os
os.makedirs("chapter_1", exist_ok=True)
for i in range(1, page_count + 1):
filename = f"{i:03d}.jpg"
img_url = f"{chapter_url}/{filename}?sig={sig}&expires={expires}"
img_data = requests.get(img_url)
with open(f"chapter_1/{filename}","wb") as f:
f.write(img_data.content)
print(f"Downloaded {filename}")
print(f"Saved {page_count} images to chapter_1/")