Developer Portal
Integrate your applications with AniPortal's high-performance API. Access real-time anime data, schedules, and user management features with ease.
Anime & Search
Manage and search through our extensive database of anime titles.
/api/anime
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.
/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.
/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.
GET /api
Returns the full list of all available endpoints — useful for dynamic help menus.
/api/search?q={title}
Search anime by title. Returns matching results with IDs, slugs, posters.
/api/anime/{id}
Get full anime details by numeric ID (e.g. 1) or slug (e.g. solo-leveling).
/api/anime/{slug}/episodes
Get all published episodes for an anime, including streaming sources.
/api/episode/{id}
Get a single episode with full source URLs — perfect for sharing direct streaming links.
/api/genre/{genre}
Filter anime by genre (e.g. action, romance, comedy).
/api/trending?limit=5
Top trending anime sorted by views, episodes, and featured status.
/api/latest?limit=5
Most recently added anime titles.
/api/random
Get a random anime — great for "surprise me" bot commands.
/api/airing
All locally tracked currently-airing (Ongoing) anime.
/api/anilist/airing-today
Live schedule from AniList — what's airing today across all timezones.
/api/anilist/upcoming?limit=10
Next N upcoming episode airings from AniList with countdown data.
/api/anilist/schedule
Full weekly schedule grouped by day from AniList.
/api/user/{id}
Get public user profile (name, avatar, level, XP, status).
/api/user/{id}/watchlist
User's watchlist with anime titles and status (watching, completed, etc).
/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:
<?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_markupparameter
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.
/aniportal-api/auth/register
Create a new account.
{ "email": "user@example.com", "password": "secret123", "username": "optional" }
/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": "..." } }
/aniportal-api/auth/logout
Invalidate session. Requires Bearer token.
/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.
/aniportal-api/anime?page=1&limit=20
Paginated list of all published anime. Defaults: page=1, limit=20 (max 50).
/aniportal-api/anime/{slug}
Full anime details with genres and all episodes.
/aniportal-api/anime/{slug}/episodes?page=1
Paginated episodes list for an anime.
/aniportal-api/search?q=naruto&page=1
Search anime by title, description, or studio.
/aniportal-api/genres
List all genres with anime count.
/aniportal-api/genre/action?page=1
Paginated anime filtered by genre slug.
/aniportal-api/discover?sort=popular|trending|recent
Curated lists. popular = weighted by rating+views+episodes; trending = last 30 days; recent = newest first.
/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.
/aniportal-api/user/watchlist
Get current user's watchlist with anime details.
/aniportal-api/user/watchlist
Add or update watchlist item.
{ "animeId": 1, "status": "watching" }
Status: watching, completed, plan_to_watch, dropped
/aniportal-api/user/watchlist/{animeId}
Remove an anime from the watchlist.
/aniportal-api/user/history?limit=20
Watch history with episode and anime details.
/aniportal-api/user/history
Save watch progress.
{ "episodeId": 5, "progress": 360, "completed": false }
/aniportal-api/user/downloads
Request a download URL for an episode.
{ "episodeId": 5 }
Playback
Auth required. Returns HLS stream URL and subtitles.
/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.