Logo
Home Schedule Forum Me

Developer Portal

Integrate your applications with AniPortal's high-performance API. Access real-time anime data, schedules, and user management features with ease.

API Status: Stable
Version: v2.1.0

Anime & Search

Manage and search through our extensive database of anime titles.

GET /api/anime
Public Access

Returns a list of all published anime titles. You can filter the results using the q parameter.

Parameters

q

String | Optional

Search query for title, studio, or genre.

slug

String | Optional

Get a specific anime by its URL slug.

// Example Response
{
  "success": true,
  "data": [
    { "id": 1, "title": "Solo Leveling", "slug": "solo-leveling", ... }
  ]
}

Episodes & Video

Retrieve episode lists and secure video streaming sources.

GET /api/episodes

Parameters

anime_slug

String | Required

The slug of the anime to fetch episodes for.

Watchlist Services

Manage user bookmarks and watch status programmatically.

POST /api/watchlist

Add, update, or remove items from the authenticated user's watchlist.

JSON Body

{
  "anime_id": 102,
  "status": "watching" // or "plan_to_watch", "completed", "remove"
}

Telegram Bot Endpoints

All API endpoints designed specifically for Telegram bot integration.

ANY GET /api

Returns the full list of all available endpoints — useful for dynamic help menus.

GET /api/search?q={title}

Search anime by title. Returns matching results with IDs, slugs, posters.

GET /api/anime/{id}

Get full anime details by numeric ID (e.g. 1) or slug (e.g. solo-leveling).

GET /api/anime/{slug}/episodes

Get all published episodes for an anime, including streaming sources.

GET /api/episode/{id}

Get a single episode with full source URLs — perfect for sharing direct streaming links.

GET /api/genre/{genre}

Filter anime by genre (e.g. action, romance, comedy).

GET /api/trending?limit=5

Top trending anime sorted by views, episodes, and featured status.

GET /api/latest?limit=5

Most recently added anime titles.

GET /api/random

Get a random anime — great for "surprise me" bot commands.

GET /api/airing

All locally tracked currently-airing (Ongoing) anime.

GET /api/anilist/airing-today

Live schedule from AniList — what's airing today across all timezones.

GET /api/anilist/upcoming?limit=10

Next N upcoming episode airings from AniList with countdown data.

GET /api/anilist/schedule

Full weekly schedule grouped by day from AniList.

GET /api/user/{id}

Get public user profile (name, avatar, level, XP, status).

GET /api/user/{id}/watchlist

User's watchlist with anime titles and status (watching, completed, etc).

GET /api/stats

Site-wide statistics: total anime, episodes, users, comments, forum posts, views.

Telegram Bot Deployment Guide

Step-by-step guide to deploying a Telegram bot powered by the AniPortal API.

1 Create a Telegram Bot

Message @BotFather on Telegram and send /newbot. Follow the prompts to get your bot token.

2 Create the Bot Script

Create a new file telegram-bot.php in your AniPortal root directory with the following code:

telegram-bot.php
<?php
// Telegram Bot — Webhook handler for AniPortal
// Set webhook: https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://yourdomain.com/telegram-bot.php

define('BOT_TOKEN', 'YOUR_BOT_TOKEN_HERE');
define('API_BASE', 'https://aniportal.bio6.top'); // your AniPortal domain

function bot_send(int $chatId, string $text, array $extra = []): void
{
    $data = array_merge(['chat_id' => $chatId, 'text' => $text, 'parse_mode' => 'HTML'], $extra);
    $ch = curl_init("https://api.telegram.org/bot" . BOT_TOKEN . "/sendMessage");
    curl_setopt_array($ch, [
        CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data,
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10,
    ]);
    curl_exec($ch); curl_close($ch);
}

function api_get(string $path): ?array
{
    $ch = curl_init(API_BASE . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10,
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    $body = curl_exec($ch); curl_close($ch);
    $data = json_decode($body, true);
    return ($data['success'] ?? false) ? $data['data'] : null;
}

$input = json_decode(file_get_contents('php://input'), true);
$message = $input['message'] ?? [];
$chatId = $message['chat']['id'] ?? 0;
$text = trim($message['text'] ?? '');
if (!$chatId || !$text) exit;

$parts = explode(' ', $text, 2);
$command = strtolower($parts[0]);
$args = $parts[1] ?? '';

switch ($command) {
    case '/start':
        bot_send($chatId, "Welcome to AniPortal Bot!\n\nCommands:\n/search <title> — Search anime\n/trending — Trending anime\n/latest — Latest anime\n/random — Random anime\n/airing — Airing today\n/anime <id> — Anime details\n/episodes <slug> — Episodes list\n/genre <name> — Anime by genre\n/stats — Site statistics\n/help — This message");
        break;

    case '/help':
        bot_send($chatId, "Available commands:\n/search <query>\n/trending\n/latest\n/random\n/airing\n/anime <id_or_slug>\n/episodes <slug>\n/genre <name>\n/stats");
        break;

    case '/search':
        if (!$args) { bot_send($chatId, "Usage: /search <anime title>"); break; }
        $results = api_get("/api/search?q=" . urlencode($args));
        if (!$results) { bot_send($chatId, "No results found."); break; }
        $reply = "Search results:\n\n";
        foreach (array_slice($results, 0, 10) as $a) {
            $reply .= "• {$a['title']} (ID: {$a['id']})\n";
        }
        bot_send($chatId, $reply);
        break;

    case '/trending':
        $data = api_get('/api/trending?limit=5');
        if (!$data) { bot_send($chatId, "Could not fetch trending."); break; }
        $reply = "Trending Anime:\n\n";
        foreach ($data as $i => $a) {
            $reply .= ($i + 1) . ". {$a['title']} — " . round($a['mean_score'] ?? 0) . "/10\n";
        }
        bot_send($chatId, $reply);
        break;

    case '/random':
        $a = api_get('/api/random');
        if (!$a) { bot_send($chatId, "Could not fetch random anime."); break; }
        $reply = "Random Anime:\n\n{$a['title']}\nScore: " . round($a['mean_score'] ?? 0) . "/10\nGenre: " . (is_array($a['genres']) ? implode(', ', $a['genres']) : 'N/A');
        if (!empty($a['poster_url'])) {
            bot_send($chatId, $reply, [
                'reply_markup' => json_encode(['inline_keyboard' => [[['text' => 'View Details', 'url' => API_BASE . '/info/' . $a['slug']]]]]),
            ]);
        } else {
            bot_send($chatId, $reply);
        }
        break;

    case '/anime':
        if (!$args) { bot_send($chatId, "Usage: /anime <id_or_slug>"); break; }
        $a = ctype_digit($args) ? api_get("/api/anime/$args") : api_get("/api/anime/" . urlencode($args));
        if (!$a) { bot_send($chatId, "Anime not found."); break; }
        $reply = "{$a['title']}\n";
        if (!empty($a['series_title'])) $reply .= "Series: {$a['series_title']}\n";
        $reply .= "Score: " . round($a['mean_score'] ?? 0) . "/10\nStatus: {$a['airing_status']}\nGenre: " . (is_array($a['genres']) ? implode(', ', $a['genres']) : 'N/A');
        bot_send($chatId, $reply, [
            'reply_markup' => json_encode(['inline_keyboard' => [[['text' => 'View Episodes', 'url' => API_BASE . '/info/' . $a['slug']]]]]),
        ]);
        break;

    case '/airing':
        $schedule = api_get('/api/anilist/airing-today');
        if (!$schedule || empty($schedule)) { bot_send($chatId, "No anime airing today."); break; }
        $reply = "Airing Today:\n\n";
        foreach (array_slice($schedule, 0, 15) as $a) {
            $reply .= "• {$a['title']['romaji']} — Ep {$a['next_episode']}\n";
        }
        bot_send($chatId, $reply);
        break;

    case '/stats':
        $s = api_get('/api/stats');
        if (!$s) { bot_send($chatId, "Could not fetch stats."); break; }
        bot_send($chatId, "AniPortal Statistics:\n\nAnime: {$s['total_anime']}\nEpisodes: {$s['total_episodes']}\nUsers: {$s['total_users']}\nComments: {$s['total_comments']}\nForum Posts: {$s['total_forum_posts']}\nTotal Views: {$s['total_views']}");
        break;

    default:
        bot_send($chatId, "Unknown command. Send /help to see available commands.");
}

3 Set the Webhook

Tell Telegram to send updates to your bot URL. Open this in your browser:

https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook?url=https://yourdomain.com/telegram-bot.php

Replace <YOUR_BOT_TOKEN> and https://yourdomain.com with your actual values. On success you'll see: {"ok":true,"result":true,"description":"Webhook was set"}

4 Testing Your Bot

Open your bot on Telegram and send /start. Try these commands:

/search solo leveling

Search for anime by title

/trending

Top trending anime right now

/random

Discover a random anime

/anime 1

Get details by ID or slug

/airing

What's airing today on AniList

/stats

AniPortal site statistics

5 Extending the Bot

You can easily add more commands using the existing API endpoints:

  • /episodes <slug> — Fetch episode list using /api/anime/{slug}/episodes
  • /genre <name> — Browse by genre using /api/genre/{genre}
  • /latest — Latest anime using /api/latest?limit=5
  • /schedule — Weekly schedule using /api/anilist/schedule
  • /upcoming — Upcoming episodes using /api/anilist/upcoming?limit=10
  • /watchlist <user_id> — User watchlist using /api/user/{id}/watchlist
  • • Inline buttons, images, and polls can be added via Telegram's reply_markup parameter

REST API MySQL + JWT

A full-featured REST API with JWT authentication, pagination, and PDO prepared statements. Located at /aniportal-api/.

Getting Started

1. Import schema.sql into your MySQL database

2. Edit config.php with your DB credentials and JWT secret

3. Place aniportal-api/ in your web root (or point a subdomain to it)

4. All endpoints are prefixed with /aniportal-api/

Response Format

// Success
{ "status": "success", "data": { ... } }

// Error  
{ "status": "error", "error": "message", "code": "ERROR_CODE" }

// Paginated list
{ "data": [...], "total": 100, "page": 1, "totalPages": 5 }

Authentication

Public endpoints — no token required.

POST /aniportal-api/auth/register

Create a new account.

{ "email": "user@example.com", "password": "secret123", "username": "optional" }
POST /aniportal-api/auth/login

Returns a JWT token (30 day expiry) and user object.

{ "email": "user@example.com", "password": "secret123" }
{ "token": "eyJ...", "user": { "id": 1, "username": "user", "email": "..." } }
POST /aniportal-api/auth/logout

Invalidate session. Requires Bearer token.

GET /aniportal-api/auth/me

Get the authenticated user's profile. Requires Authorization: Bearer <token> header.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Anime & Discovery

Public endpoints — no auth required.

GET /aniportal-api/anime?page=1&limit=20

Paginated list of all published anime. Defaults: page=1, limit=20 (max 50).

GET /aniportal-api/anime/{slug}

Full anime details with genres and all episodes.

GET /aniportal-api/anime/{slug}/episodes?page=1

Paginated episodes list for an anime.

GET /aniportal-api/search?q=naruto&page=1

Search anime by title, description, or studio.

GET /aniportal-api/genres

List all genres with anime count.

GET /aniportal-api/genre/action?page=1

Paginated anime filtered by genre slug.

GET /aniportal-api/discover?sort=popular|trending|recent

Curated lists. popular = weighted by rating+views+episodes; trending = last 30 days; recent = newest first.

GET /aniportal-api/schedule?day=monday

Broadcast schedule by day. Omit day to get all 7 days.

User Services

Auth required — include Authorization: Bearer <token> header.

GET /aniportal-api/user/watchlist

Get current user's watchlist with anime details.

POST /aniportal-api/user/watchlist

Add or update watchlist item.

{ "animeId": 1, "status": "watching" }

Status: watching, completed, plan_to_watch, dropped

DELETE /aniportal-api/user/watchlist/{animeId}

Remove an anime from the watchlist.

GET /aniportal-api/user/history?limit=20

Watch history with episode and anime details.

POST /aniportal-api/user/history

Save watch progress.

{ "episodeId": 5, "progress": 360, "completed": false }
POST /aniportal-api/user/downloads

Request a download URL for an episode.

{ "episodeId": 5 }

Playback

Auth required. Returns HLS stream URL and subtitles.

GET /aniportal-api/play/{episodeId}

Returns the HLS streaming URL, duration, and subtitle tracks.

{
  "hlsUrl": "https://cdn.example.com/hls/episode5.m3u8",
  "duration": 1380,
  "subtitles": [
    { "lang": "en", "url": "https://.../subs/en.vtt" },
    { "lang": "ja", "url": "https://.../subs/ja.vtt" }
  ],
  "episode": { "id": 5, "episode_number": 1, "title": "Beginning" },
  "anime": { "title": "Solo Leveling", "slug": "solo-leveling" }
}

Ready to build?

Join our developer community and start creating amazing apps with AniPortal.