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 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/")