diff --git a/CHANGELOG.md b/CHANGELOG.md index a4fe348..5594630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.7.2] - 2026-06-08 + +### Added +- `docs/` folder with complete API documentation + - `docs/index.md` — Overview, quick start, features + - `docs/endpoints.md` — Full API reference (27 endpoints documented) + - `docs/streaming.md` — Streaming flow guide (3-step process) + - `docs/examples.md` — Code examples in cURL, JavaScript, Python, Node.js + - `docs/architecture.md` — Project structure, tech stack, design decisions + +### Fixed +- `episodeList.extractor.js` — Parse AJAX JSON response, extract `server_ids`, `timestamp`, `mal_id` +- `streamInfo.extractor.js` — Parse server list HTML into structured objects +- `streamInfo.extractor.js` — Parse stream URL from JSON response properly +- Added cheerio import to streamInfo extractor for HTML parsing + ## [1.7.1] - 2026-06-08 ### Changed diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..1c40e56 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,256 @@ +# Architecture + +> Project structure, tech stack, and design decisions. + +## Tech Stack + +| Component | Technology | +|-----------|------------| +| Runtime | Node.js | +| Framework | Express.js | +| Scraper | Cheerio + Axios | +| Deployment | Vercel (Serverless) | +| Language | JavaScript (ES Modules) | + +## Project Structure + +``` +AniKotoAPI/ +├── server.js # Express entry point +├── vercel.json # Vercel deployment config +├── package.json # Dependencies and scripts +├── public/ # Static files +│ ├── index.html # Landing page +│ ├── 404.html # Error page +│ ├── manifest.json # PWA manifest +│ ├── robots.txt # Crawler directives +│ ├── sitemap.xml # XML sitemap +│ ├── og-image.svg # Open Graph image +│ ├── privacy.html # Privacy policy +│ └── tos.html # Terms of service +├── src/ +│ ├── configs/ +│ │ ├── dataUrl.js # URL patterns for anikototv.to +│ │ ├── header.config.js # Request headers +│ │ └── ids.config.js # Genre/type/status ID mappings +│ ├── controllers/ +│ │ ├── homeInfo.controller.js +│ │ ├── animeInfo.controller.js +│ │ ├── search.controller.js +│ │ ├── episodeList.controller.js +│ │ ├── episodeListAjax.controller.js +│ │ ├── streamInfo.controller.js +│ │ ├── schedule.controller.js +│ │ ├── spotlight.controller.js +│ │ ├── trending.controller.js +│ │ ├── topten.controller.js +│ │ ├── suggestion.controller.js +│ │ ├── random.controller.js +│ │ ├── popular.controller.js +│ │ ├── filter.controller.js +│ │ ├── watchPage.controller.js +│ │ ├── azList.controller.js +│ │ ├── newRelease.controller.js +│ │ ├── status.controller.js +│ │ ├── trendingSidebar.controller.js +│ │ ├── seasons.controller.js +│ │ └── watchOrder.controller.js +│ ├── extractors/ +│ │ ├── homeInfo.extractor.js +│ │ ├── animeInfo.extractor.js +│ │ ├── search.extractor.js +│ │ ├── episodeList.extractor.js +│ │ ├── streamInfo.extractor.js +│ │ ├── schedule.extractor.js +│ │ ├── spotlight.extractor.js +│ │ ├── trending.extractor.js +│ │ ├── topten.extractor.js +│ │ ├── suggestion.extractor.js +│ │ ├── random.extractor.js +│ │ ├── popular.extractor.js +│ │ ├── filter.extractor.js +│ │ ├── watchPage.extractor.js +│ │ ├── azList.extractor.js +│ │ ├── newRelease.extractor.js +│ │ ├── status.extractor.js +│ │ ├── trendingSidebar.extractor.js +│ │ ├── seasons.extractor.js +│ │ └── watchOrder.extractor.js +│ ├── routes/ +│ │ ├── apiRoutes.js # Main API router +│ │ └── category.route.js # Genre/type/status routes +│ └── helper/ +│ └── cache.helper.js # In-memory cache +└── docs/ # Documentation + ├── index.md + ├── endpoints.md + ├── streaming.md + ├── examples.md + └── architecture.md +``` + +## Request Flow + +``` +Client Request + │ + ▼ +┌─────────────┐ +│ vercel.json │ ─── /api/* ──→ server.js +│ (routing) │ ─── /* ─────→ public/ +└─────────────┘ + │ + ▼ +┌─────────────┐ +│ server.js │ +│ (Express) │ +└─────────────┘ + │ + ▼ +┌─────────────┐ +│ apiRoutes.js │ ─── Route matching +└─────────────┘ + │ + ▼ +┌─────────────┐ +│ Controllers │ ─── Business logic, cache check +└─────────────┘ + │ + ▼ +┌─────────────┐ +│ Extractors │ ─── HTTP request + HTML parsing +└─────────────┘ + │ + ▼ +┌─────────────┐ +│ anikototv.to │ ─── Source data +└─────────────┘ +``` + +## Caching + +The API uses an in-memory cache with a 5-minute TTL: + +```javascript +// Cache key format: {endpoint}_{id} +// Examples: +// "episodes_naruto-shippuden" +// "stream_MTF1dkFtaW9..." +// "servers_SlNVT25..." +``` + +**Characteristics:** +- Stored in a JavaScript `Map` +- TTL: 5 minutes (300,000 ms) +- Reset on server restart (serverless = every cold start) +- Key format: `{type}_{identifier}` + +## Source Site Structure + +The API scrapes data from `anikototv.to`: + +| Page | URL Pattern | Data | +|------|-------------|------| +| Home | `/home` | Spotlight, trending, popular | +| Watch | `/watch/{slug}` | Anime info, anime ID | +| Episode | `/watch/{slug}/ep-{n}` | Episode data | +| Search | `/filter?keyword={q}` | Search results | +| Genre | `/genre/{name}` | Genre listings | +| Type | `/type/{name}` | Type listings | +| Status | `/status/{name}` | Status listings | +| A-Z | `/az-list/{letter}` | Alphabetical list | + +**AJAX Endpoints:** + +| Endpoint | URL Pattern | Data | +|----------|-------------|------| +| Episodes | `/ajax/episode/list/{animeId}` | Episode list HTML | +| Servers | `/ajax/server/list?servers={ids}` | Server list HTML | +| Stream | `/ajax/server?get={linkId}` | Stream URL JSON | + +## ID Mappings + +Genre IDs (partial): + +```javascript +{ + action: 1, + adventure: 2, + comedy: 4, + demons: 5, + drama: 8, + fantasy: 10, + isekai: 19, + romance: 22, + shounen: 27, + supernatural: 37 +} +``` + +Type IDs: + +```javascript +{ + movie: 1, + music: 2, + ona: 3, + ova: 4, + special: 5, + tv: 6 +} +``` + +Status IDs: + +```javascript +{ + "currently-airing": 1, + "finished-airing": 2, + "not-yet-aired": 3 +} +``` + +## Vercel Configuration + +```json +{ + "version": 2, + "builds": [ + { + "src": "server.js", + "use": "@vercel/node" + } + ], + "routes": [ + { + "src": "/api/(.*)", + "dest": "server.js", + "headers": { "Access-Control-Allow-Origin": "*" } + }, + { + "src": "/(.*)", + "dest": "server.js" + } + ] +} +``` + +## Error Handling + +All errors follow this format: + +```json +{ + "success": false, + "message": "Error description" +} +``` + +Common errors: + +| Status | Message | Cause | +|--------|---------|-------| +| 400 | "Link ID is required" | Missing `id` parameter | +| 400 | "Episode IDs are required" | Missing `ids` parameter | +| 400 | "Anime slug is required" | Missing anime slug | +| 500 | "Internal server error" | Scraping failed or site down | diff --git a/docs/endpoints.md b/docs/endpoints.md new file mode 100644 index 0000000..330225e --- /dev/null +++ b/docs/endpoints.md @@ -0,0 +1,857 @@ +# API Endpoints Reference + +> Complete documentation for all AniKotoAPI endpoints. + +**Base URL:** `https://anikototvapi.vercel.app/api` + +--- + +## Table of Contents + +- [Home](#home) +- [Search](#search) +- [Search Suggestions](#search-suggestions) +- [Anime Info](#anime-info) +- [Episodes](#episodes) +- [Episodes AJAX](#episodes-ajax) +- [Watch Page](#watch-page) +- [Stream Info](#stream-info) +- [Server List](#server-list) +- [Mapper Servers](#mapper-servers) +- [Schedule](#schedule) +- [Spotlight](#spotlight) +- [Trending](#trending) +- [Top 10](#top-10) +- [Most Popular](#most-popular) +- [New Release](#new-release) +- [Newly Added](#newly-added) +- [Random](#random) +- [Filter](#filter) +- [Suggestions](#suggestions) +- [Trending Sidebar](#trending-sidebar) +- [Seasons](#seasons) +- [Watch Order](#watch-order) +- [A-Z List](#a-z-list) +- [Genre](#genre) +- [Type](#type) +- [Status](#status) + +--- + +## Home + +Get homepage data including spotlight, trending, popular, top-airing, genres, and more. + +``` +GET /api/ +``` + +**Parameters:** None + +**Example:** + +```bash +curl https://anikototvapi.vercel.app/api +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api"); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api") +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "spotlight": [...], + "trending": [...], + "popular": [...], + "topAiring": [...], + "genres": [...] + } +} +``` + +--- + +## Search + +Search for anime by keyword with pagination. + +``` +GET /api/search +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `keyword` | string | Yes | — | Search query | +| `page` | number | No | 1 | Page number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/search?keyword=naruto&page=1" +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/search?keyword=naruto"); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/search", params={"keyword": "naruto"}) +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "totalPages": 10, + "data": [ + { + "title": "Naruto", + "id": "naruto-xxxxx", + "slug": "naruto-xxxxx/ep-1", + "image": "https://...", + "type": "TV", + "genres": ["Action", "Adventure"] + } + ] + } +} +``` + +--- + +## Search Suggestions + +Get autocomplete suggestions for search queries. + +``` +GET /api/search/suggest +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `keyword` | string | Yes | Partial search query | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/search/suggest?keyword=dea" +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/search/suggest?keyword=dea"); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/search/suggest", params={"keyword": "dea"}) +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": [ + { "title": "Death Note", "slug": "death-note-xxxxx" }, + { "title": "Deadman Wonderland", "slug": "deadman-wonderland-xxxxx" } + ] +} +``` + +--- + +## Anime Info + +Get detailed information about a specific anime. + +``` +GET /api/info +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | Anime slug | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/info?id=naruto-shippuden" +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/info?id=naruto-shippuden"); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/info", params={"id": "naruto-shippuden"}) +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "title": "Naruto: Shippuuden", + "japaneseTitle": "ナルト- 疾風伝", + "image": "https://...", + "synopsis": "...", + "type": "TV", + "status": "Finished Airing", + "totalEpisodes": 500, + "genres": ["Action", "Adventure"], + "malId": 1735, + "relations": [...], + "recommendations": [...] + } +} +``` + +--- + +## Episodes + +Get the episode list for an anime. + +``` +GET /api/episodes/:id +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | Anime slug (path parameter) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/episodes/naruto-shippuden" +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/episodes/naruto-shippuden"); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/episodes/naruto-shippuden") +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "animeId": 12345, + "slug": "naruto-shippuden", + "totalEpisodes": 500, + "episodes": [ + { + "id": "110289", + "episode_no": 1, + "slug": "1", + "title": "", + "active": true, + "href": "#", + "server_ids": "SlNVT25...", + "timestamp": "1729249503", + "mal_id": "1735" + } + ] + } +} +``` + +--- + +## Episodes AJAX + +Get the AJAX-loaded episode list (same data as episodes, but fetched via AJAX endpoint). + +``` +GET /api/episodes-ajax/:id +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | Anime ID (numeric, from episodes response) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/episodes-ajax/12345" +``` + +--- + +## Watch Page + +Get watch page data for a specific episode. + +``` +GET /api/watch +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | Anime slug | +| `ep` | number | No | Episode number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/watch?id=naruto-shippuden&ep=1" +``` + +--- + +## Stream Info + +Get the streaming URL and metadata for an episode server. + +``` +GET /api/stream +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | string | Yes | Server link ID (from servers response) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/stream?id=MTF1dkFtaW9..." +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/stream?id=MTF1dkFtaW9..."); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/stream", params={"id": "MTF1dkFtaW9..."}) +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "linkId": "MTF1dkFtaW9...", + "url": "https://megaplay.buzz/stream/s-5/94736/sub", + "skipData": { + "intro": [0, 0], + "outro": [0, 0] + } + } +} +``` + +--- + +## Server List + +Get available streaming servers for an episode. + +``` +GET /api/servers +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `ids` | string | Yes | Server IDs (from episodes response `server_ids` field) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/servers?ids=SlNVT25..." +``` + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/servers?ids=SlNVT25..."); +const data = await res.json(); +``` + +```python +import requests +r = requests.get("https://anikototvapi.vercel.app/api/servers", params={"ids": "SlNVT25..."}) +data = r.json() +``` + +**Response:** + +```json +{ + "success": true, + "results": [ + { + "type": "sub", + "ep_id": "110289", + "link_id": "MTF1dkFtaW9...", + "cmid": "animixplay-xxxxx", + "sv_id": "323", + "name": "HD-1" + }, + { + "type": "sub", + "ep_id": "110289", + "link_id": "MTF1dkFtaW9...", + "cmid": "animixplay-xxxxx", + "sv_id": "e54", + "name": "Vidstream-2" + } + ] +} +``` + +--- + +## Mapper Servers + +Get alternative streaming servers from the nekostream mapper API. + +``` +GET /api/mapper-servers +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `malId` | number | Yes | MyAnimeList ID | +| `slug` | string | Yes | Anime slug | +| `timestamp` | string | Yes | Episode timestamp | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/mapper-servers?malId=1735&slug=naruto-shippuden×tamp=1729249503" +``` + +**Response:** + +```json +{ + "success": true, + "results": [ + { + "provider": "vidstreaming", + "type": "sub", + "url": "https://...", + "download": "https://..." + } + ] +} +``` + +--- + +## Schedule + +Get anime airing schedule. + +``` +GET /api/schedule +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `date` | string | No | Today | Date in YYYY-MM-DD format | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/schedule" +curl "https://anikototvapi.vercel.app/api/schedule?date=2026-06-08" +``` + +--- + +## Spotlight + +Get spotlight/featured anime carousel. + +``` +GET /api/spotlight +``` + +**Parameters:** None + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/spotlight" +``` + +--- + +## Trending + +Get currently trending anime. + +``` +GET /api/trending +``` + +**Parameters:** None + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/trending" +``` + +--- + +## Top 10 + +Get top 10 ranked anime (today, week, month). + +``` +GET /api/top-ten +``` + +**Parameters:** None + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/top-ten" +``` + +--- + +## Most Popular + +Get most popular anime list. + +``` +GET /api/most-popular +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `page` | number | No | 1 | Page number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/most-popular?page=1" +``` + +--- + +## New Release + +Get recently released episodes. + +``` +GET /api/new-release +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `page` | number | No | 1 | Page number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/new-release" +``` + +--- + +## Newly Added + +Get newly added anime series. + +``` +GET /api/newly-added +``` + +**Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `page` | number | No | 1 | Page number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/newly-added" +``` + +--- + +## Random + +Get random anime information. + +``` +GET /api/random +``` + +**Parameters:** None + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/random" +``` + +--- + +## Filter + +Filter anime by genre, type, status, season, and more. + +``` +GET /api/filter +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `keyword` | string | Yes | Search keyword (can be empty) | +| `genre[]` | number | No | Genre IDs (multiple supported) | +| `type[]` | number | No | Type IDs (multiple supported) | +| `status[]` | number | No | Status IDs (multiple supported) | +| `sort` | string | No | Sort order | +| `season` | string | No | Season | +| `page` | number | No | Page number | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/filter?keyword=&genre[]=1&genre[]=4" +``` + +--- + +## Suggestions + +Get anime suggestions by keyword. + +``` +GET /api/suggestions +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `keyword` | string | Yes | Search keyword | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/suggestions?keyword=naruto" +``` + +--- + +## Trending Sidebar + +Get trending sidebar widget data. + +``` +GET /api/trending-sidebar +``` + +**Parameters:** None + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/trending-sidebar" +``` + +--- + +## Seasons + +Get season information for an anime. + +``` +GET /api/seasons/:id +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | number | Yes | Anime ID (path parameter) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/seasons/12345" +``` + +--- + +## Watch Order + +Get recommended watch order for an anime. + +``` +GET /api/watch-order/:id +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `id` | number | Yes | Anime ID (path parameter) | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/watch-order/12345" +``` + +--- + +## A-Z List + +Get anime listing alphabetically. + +``` +GET /api/az-list/:letter +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `letter` | string | Yes | Letter or "other" for non-alpha titles | + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/az-list/a" +curl "https://anikototvapi.vercel.app/api/az-list/other" +``` + +--- + +## Genre + +Get anime by genre. + +``` +GET /api/genre/:name +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | Genre slug | + +**Available Genres:** + +`action`, `adventure`, `cars`, `comedy`, `dementia`, `demons`, `drama`, `ecchi`, `fantasy`, `game`, `harem`, `historical`, `horror`, `isekai`, `josei`, `kids`, `magic`, `mahou-shoujo`, `martial-arts`, `mecha`, `military`, `music`, `mystery`, `parody`, `police`, `psychological`, `romance`, `samurai`, `school`, `sci-fi`, `seinen`, `shoujo`, `shoujo-ai`, `shounen`, `shounen-ai`, `slice-of-life`, `space`, `sports`, `super-power`, `supernatural`, `thriller`, `unknown`, `vampire` + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/genre/action" +``` + +--- + +## Type + +Get anime by type. + +``` +GET /api/type/:name +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | Type slug | + +**Available Types:** `movie`, `music`, `ona`, `ova`, `special`, `tv` + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/type/tv" +``` + +--- + +## Status + +Get anime by airing status. + +``` +GET /api/status/:name +``` + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | Status slug | + +**Available Statuses:** `currently-airing`, `finished-airing`, `not-yet-aired` + +**Example:** + +```bash +curl "https://anikototvapi.vercel.app/api/status/currently-airing" +``` diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..2c572ff --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,291 @@ +# Code Examples + +> Working examples in cURL, JavaScript, and Python. + +## cURL + +### Get Homepage + +```bash +curl https://anikototvapi.vercel.app/api +``` + +### Search Anime + +```bash +curl "https://anikototvapi.vercel.app/api/search?keyword=naruto&page=1" +``` + +### Get Anime Info + +```bash +curl "https://anikototvapi.vercel.app/api/info?id=naruto-shippuden" +``` + +### Get Episodes + +```bash +curl "https://anikototvapi.vercel.app/api/episodes/naruto-shippuden" +``` + +### Get Servers + +```bash +curl "https://anikototvapi.vercel.app/api/servers?ids=SlNVT25..." +``` + +### Get Stream URL + +```bash +curl "https://anikototvapi.vercel.app/api/stream?id=MTF1dkFtaW9..." +``` + +### Get Schedule + +```bash +curl "https://anikototvapi.vercel.app/api/schedule" +curl "https://anikototvapi.vercel.app/api/schedule?date=2026-06-08" +``` + +### Filter by Genre + +```bash +curl "https://anikototvapi.vercel.app/api/filter?keyword=&genre[]=1&genre[]=4" +``` + +### Get Genre Anime + +```bash +curl "https://anikototvapi.vercel.app/api/genre/action" +``` + +--- + +## JavaScript (Fetch API) + +### Get Homepage + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api"); +const data = await res.json(); +console.log(data.results); +``` + +### Search Anime + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/search?keyword=naruto"); +const data = await res.json(); +data.results.data.forEach(anime => { + console.log(anime.title); +}); +``` + +### Get Anime Info + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/info?id=naruto-shippuden"); +const data = await res.json(); +console.log(data.results.title); +console.log(data.results.synopsis); +``` + +### Get Episodes + +```javascript +const res = await fetch("https://anikototvapi.vercel.app/api/episodes/naruto-shippuden"); +const data = await res.json(); +data.results.episodes.forEach(ep => { + console.log(`Episode ${ep.episode_no} - ID: ${ep.id}`); +}); +``` + +### Full Streaming Flow + +```javascript +const BASE = "https://anikototvapi.vercel.app/api"; + +async function getStream(slug, episodeNum) { + // 1. Get episodes + const epsRes = await fetch(`${BASE}/episodes/${slug}`); + const eps = await epsRes.json(); + const ep = eps.results.episodes.find(e => e.episode_no === episodeNum); + + // 2. Get servers + const srvRes = await fetch(`${BASE}/servers?ids=${ep.server_ids}`); + const srvs = await srvRes.json(); + const server = srvs.results[0]; + + // 3. Get stream + const streamRes = await fetch(`${BASE}/stream?id=${server.link_id}`); + const stream = await streamRes.json(); + + return stream.results; +} + +// Usage +getStream("naruto-shippuden", 1).then(stream => { + console.log("URL:", stream.url); + videoPlayer.src = stream.url; +}); +``` + +### Search with Suggestions + +```javascript +// Get autocomplete suggestions +const res = await fetch("https://anikototvapi.vercel.app/api/search/suggest?keyword=dea"); +const suggestions = await res.json(); +suggestions.results.forEach(s => console.log(s.title)); +``` + +--- + +## Python (requests) + +### Get Homepage + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api") +data = r.json() +print(data["results"]) +``` + +### Search Anime + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api/search", params={"keyword": "naruto"}) +data = r.json() +for anime in data["results"]["data"]: + print(anime["title"]) +``` + +### Get Anime Info + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api/info", params={"id": "naruto-shippuden"}) +data = r.json() +print(data["results"]["title"]) +print(data["results"]["synopsis"]) +``` + +### Get Episodes + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api/episodes/naruto-shippuden") +data = r.json() +for ep in data["results"]["episodes"]: + print(f"Episode {ep['episode_no']} - ID: {ep['id']}") +``` + +### Full Streaming Flow + +```python +import requests + +BASE = "https://anikototvapi.vercel.app/api" + +def get_stream(slug, episode_num): + # 1. Get episodes + eps = requests.get(f"{BASE}/episodes/{slug}").json() + ep = next(e for e in eps["results"]["episodes"] if e["episode_no"] == episode_num) + + # 2. Get servers + srvs = requests.get(f"{BASE}/servers", params={"ids": ep["server_ids"]}).json() + server = srvs["results"][0] + + # 3. Get stream + stream = requests.get(f"{BASE}/stream", params={"id": server["link_id"]}).json() + + return stream["results"] + +# Usage +stream = get_stream("naruto-shippuden", 1) +print(f"URL: {stream['url']}") +``` + +### Filter by Genre + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api/filter", params={ + "keyword": "", + "genre[]": [1, 4] # Action, Comedy +}) +data = r.json() +``` + +### Get Schedule + +```python +import requests + +r = requests.get("https://anikototvapi.vercel.app/api/schedule") +data = r.json() +``` + +--- + +## Node.js (axios) + +### Search Anime + +```javascript +const axios = require("axios"); + +const { data } = await axios.get("https://anikototvapi.vercel.app/api/search", { + params: { keyword: "naruto" } +}); +console.log(data.results.data); +``` + +### Full Streaming Flow + +```javascript +const axios = require("axios"); + +const BASE = "https://anikototvapi.vercel.app/api"; + +async function getStream(slug, episodeNum) { + const { data: eps } = await axios.get(`${BASE}/episodes/${slug}`); + const ep = eps.results.episodes.find(e => e.episode_no === episodeNum); + + const { data: srvs } = await axios.get(`${BASE}/servers`, { params: { ids: ep.server_ids } }); + const server = srvs.results[0]; + + const { data: stream } = await axios.get(`${BASE}/stream`, { params: { id: server.link_id } }); + + return stream.results; +} +``` + +--- + +## cURL (with jq) + +### Pretty Print Homepage + +```bash +curl -s https://anikototvapi.vercel.app/api | jq '.results.trending[:3]' +``` + +### Search and Extract Titles + +```bash +curl -s "https://anikototvapi.vercel.app/api/search?keyword=naruto" | jq '.results.data[].title' +``` + +### Get Stream URL Only + +```bash +curl -s "https://anikototvapi.vercel.app/api/stream?id=MTF1dkFtaW9..." | jq -r '.results.url' +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..60817dc --- /dev/null +++ b/docs/index.md @@ -0,0 +1,65 @@ +# AniKotoAPI Documentation + +> Free REST API for anime data. Browse, search, stream — no API key required. + +## Overview + +AniKotoAPI is a free, open-source REST API that provides anime data scraped from [anikototv.to](https://anikototv.to). It's built with Node.js, Express, and Cheerio, and deployed on Vercel. + +**Base URL:** `https://anikototvapi.vercel.app/api` + +## Quick Start + +```bash +# Get homepage data +curl https://anikototvapi.vercel.app/api + +# Search for anime +curl "https://anikototvapi.vercel.app/api/search?keyword=naruto" + +# Get anime info +curl "https://anikototvapi.vercel.app/api/info?id=naruto-shippuden" +``` + +## Features + +- **24+ Endpoints** — Home, search, info, episodes, streaming, schedule, and more +- **No API Key** — Just make requests, no registration needed +- **In-Memory Cache** — 5-minute TTL for fast responses +- **CORS Enabled** — Access from any domain +- **JSON Responses** — Standardized `{success, results}` format + +## Documentation + +- [API Endpoints Reference](endpoints.md) — Complete endpoint documentation +- [Streaming Flow Guide](streaming.md) — How to get stream URLs +- [Code Examples](examples.md) — cURL, JavaScript, Python +- [Architecture](architecture.md) — Project structure and design + +## Response Format + +All endpoints return JSON in this format: + +```json +{ + "success": true, + "results": { ... } +} +``` + +Error responses: + +```json +{ + "success": false, + "message": "Error description" +} +``` + +## Rate Limiting + +There are no explicit rate limits, but the API uses a 5-minute in-memory cache. Repeated requests for the same data will be served from cache. + +## Disclaimer + +This API is for **educational purposes only**. It scrapes publicly available data from anikototv.to. We are not affiliated with or endorsed by AniKoto. All content belongs to its respective owners. diff --git a/docs/streaming.md b/docs/streaming.md new file mode 100644 index 0000000..9a6bcbd --- /dev/null +++ b/docs/streaming.md @@ -0,0 +1,213 @@ +# Streaming Flow Guide + +> How to get streaming URLs from AniKotoAPI. + +## Overview + +The streaming flow involves 3 steps: + +``` +1. Get episodes → /api/episodes/{slug} +2. Get servers → /api/servers?ids={server_ids} +3. Get stream URL → /api/stream?id={link_id} +``` + +## Step 1: Get Episodes + +First, get the episode list for an anime. This returns episode data including `server_ids` needed for the next step. + +```bash +curl "https://anikototvapi.vercel.app/api/episodes/naruto-shippuden" +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "animeId": 12345, + "slug": "naruto-shippuden", + "totalEpisodes": 500, + "episodes": [ + { + "id": "110289", + "episode_no": 1, + "server_ids": "SlNVT25JaFlCMnZOeXZ2aG5t...", + "timestamp": "1729249503", + "mal_id": "1735" + } + ] + } +} +``` + +**Key fields:** +- `server_ids` — Base64-encoded string needed for the servers endpoint +- `timestamp` — Used for the mapper API +- `mal_id` — MyAnimeList ID for the anime + +## Step 2: Get Servers + +Use the `server_ids` from step 1 to get available streaming servers. + +```bash +curl "https://anikototvapi.vercel.app/api/servers?ids=SlNVT25JaFlCMnZOeXZ2aG5t..." +``` + +**Response:** + +```json +{ + "success": true, + "results": [ + { + "type": "sub", + "ep_id": "110289", + "link_id": "MTF1dkFtaW9BRTZPbzJJRElFZUZr...", + "cmid": "animixplay-xxxxx", + "sv_id": "323", + "name": "HD-1" + }, + { + "type": "sub", + "ep_id": "110289", + "link_id": "MTF1dkFtaW9BRTZPbzJJRElFZUZr...", + "cmid": "animixplay-xxxxx", + "sv_id": "e54", + "name": "Vidstream-2" + }, + { + "type": "dub", + "ep_id": "110289", + "link_id": "MTF1dkFtaW9BRTZPbzJJRElFZUZr...", + "cmid": "animixplay-xxxxx", + "sv_id": "a41", + "name": "VidCloud-1" + } + ] +} +``` + +**Key fields:** +- `type` — "sub" or "dub" +- `link_id` — ID needed for the stream endpoint +- `name` — Server name (HD-1, Vidstream-2, etc.) + +## Step 3: Get Stream URL + +Use the `link_id` from step 2 to get the actual streaming URL. + +```bash +curl "https://anikototvapi.vercel.app/api/stream?id=MTF1dkFtaW9BRTZPbzJJRElFZUZr..." +``` + +**Response:** + +```json +{ + "success": true, + "results": { + "linkId": "MTF1dkFtaW9BRTZPbzJJRElFZUZr...", + "url": "https://megaplay.buzz/stream/s-5/94736/sub", + "skipData": { + "intro": [0, 0], + "outro": [0, 0] + } + } +} +``` + +**Key fields:** +- `url` — Direct streaming URL (m3u8 or mp4) +- `skipData` — Intro/outro timestamps for skipping + +## Complete Example (JavaScript) + +```javascript +const BASE = "https://anikototvapi.vercel.app/api"; + +async function getStreamUrl(animeSlug, episodeNumber) { + // Step 1: Get episodes + const epsRes = await fetch(`${BASE}/episodes/${animeSlug}`); + const epsData = await epsRes.json(); + const episode = epsData.results.episodes.find( + ep => ep.episode_no === episodeNumber + ); + + if (!episode) throw new Error("Episode not found"); + + // Step 2: Get servers + const srvRes = await fetch(`${BASE}/servers?ids=${episode.server_ids}`); + const srvData = await srvRes.json(); + const server = srvData.results.find(s => s.type === "sub") || srvData.results[0]; + + if (!server) throw new Error("No servers available"); + + // Step 3: Get stream URL + const streamRes = await fetch(`${BASE}/stream?id=${server.link_id}`); + const streamData = await streamRes.json(); + + return streamData.results; +} + +// Usage +getStreamUrl("naruto-shippuden", 1) + .then(stream => { + console.log("Stream URL:", stream.url); + console.log("Skip intro at:", stream.skipData.intro[0]); + }); +``` + +## Complete Example (Python) + +```python +import requests + +BASE = "https://anikototvapi.vercel.app/api" + +def get_stream_url(anime_slug, episode_number): + # Step 1: Get episodes + eps_res = requests.get(f"{BASE}/episodes/{anime_slug}") + eps_data = eps_res.json() + episode = next( + ep for ep in eps_data["results"]["episodes"] + if ep["episode_no"] == episode_number + ) + + # Step 2: Get servers + srv_res = requests.get(f"{BASE}/servers", params={"ids": episode["server_ids"]}) + srv_data = srv_res.json() + server = next( + (s for s in srv_data["results"] if s["type"] == "sub"), + srv_data["results"][0] + ) + + # Step 3: Get stream URL + stream_res = requests.get(f"{BASE}/stream", params={"id": server["link_id"]}) + stream_data = stream_res.json() + + return stream_data["results"] + +# Usage +stream = get_stream_url("naruto-shippuden", 1) +print(f"Stream URL: {stream['url']}") +print(f"Skip intro at: {stream['skipData']['intro'][0]}") +``` + +## Alternative: Mapper Servers + +For additional streaming sources, use the mapper API with `malId`, `slug`, and `timestamp` from the episodes response. + +```bash +curl "https://anikototvapi.vercel.app/api/mapper-servers?malId=1735&slug=naruto-shippuden×tamp=1729249503" +``` + +This returns alternative providers like VidStreaming, Gogoanime, etc. + +## Notes + +- The `url` in stream response is typically an m3u8 playlist or direct mp4 link +- `skipData` contains intro/outro timestamps in seconds +- Some servers may require specific headers or referrers to work +- The mapper API is external and may not always be available