Files
AniKotoAPI/public/index.html
T
Shinei Nouzen 378f9b236a feat: redesign HTML pages with Claude/Anthropic design system
- Warm cream canvas (#faf9f5) with coral accent (#cc785c)
- Slab-serif headlines (Cormorant Garamond) + humanist sans body (Inter)
- Dark navy product surfaces (#181715) for code blocks
- Light cream feature cards (#efe9de)
- Editorial, literary aesthetic
- Removed ambient orbs and particles
- Minimal shadows, color-block depth
2026-07-05 17:31:17 +05:30

678 lines
50 KiB
HTML

<!DOCTYPE html>
<html lang="en" prefix="og: https://ogp.me/ns#">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AniKotoAPI — Free Anime Data API</title>
<meta property="og:title" content="AniKotoAPI — Free Anime Data API" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://anikototvapi.vercel.app" />
<meta property="og:image" content="https://anikototvapi.vercel.app/share.png" />
<meta property="og:description" content="Free REST API for anime data. Browse, search, stream episodes. 33+ endpoints, no API key required." />
<meta property="og:site_name" content="AniKotoAPI" />
<meta property="og:locale" content="en_US" />
<meta property="og:image:secure_url" content="https://anikototvapi.vercel.app/share.png" />
<meta property="og:image:type" content="image/jpeg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="601" />
<meta property="og:image:alt" content="AniKotoAPI - Free Anime Data REST API" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@Shineii86" />
<meta name="twitter:creator" content="@Shineii86" />
<meta name="twitter:title" content="AniKotoAPI — Free Anime Data API" />
<meta name="twitter:description" content="Free REST API for anime data. Browse, search, stream episodes. 33+ endpoints, no API key required." />
<meta name="twitter:image" content="https://anikototvapi.vercel.app/share.png" />
<meta name="twitter:image:alt" content="AniKotoAPI - Free Anime Data REST API" />
<meta name="twitter:domain" content="anikototvapi.vercel.app" />
<meta name="description" content="Free REST API for anime data. Browse, search, stream episodes. 33+ endpoints, no API key required. Built with Node.js, Express, Cheerio." />
<meta name="author" content="Shinei Nouzen" />
<meta name="keywords" content="anime api, free api, rest api, anime data, streaming, episode servers, nodejs, express, anime database, anime search" />
<meta name="robots" content="index, follow" />
<meta name="theme-color" content="#faf9f5" />
<link rel="canonical" href="https://anikototvapi.vercel.app" />
<link rel="icon" type="image/png" href="/favicon-origin.png" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon-origin.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
:root{
--canvas:#faf9f5;--surface-soft:#f5f0e8;--surface-card:#efe9de;--surface-cream-strong:#e8e0d2;
--surface-dark:#181715;--surface-dark-elevated:#252320;--surface-dark-soft:#1f1e1b;
--hairline:#e6dfd8;--hairline-soft:#ebe6df;
--ink:#141413;--body-strong:#252523;--body:#3d3d3a;--muted:#6c6a64;--muted-soft:#8e8b82;
--on-primary:#fff;--on-dark:#faf9f5;--on-dark-soft:#a09d96;
--primary:#cc785c;--primary-active:#a9583e;--primary-disabled:#e6dfd8;
--accent-teal:#5db8a6;--accent-amber:#e8a55a;
--success:#5db872;--warning:#d4a017;--error:#c64545;
--font-display:'Cormorant Garamond',Garamond,'Times New Roman',serif;
--font-body:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
--font-code:'JetBrains Mono',monospace;
}
html{scroll-behavior:smooth;-webkit-font-smoothing:antialiased}
body{font-family:var(--font-body);background:var(--canvas);color:var(--body);line-height:1.6}
::selection{background:rgba(204,120,92,.2);color:var(--ink)}
::-webkit-scrollbar{width:6px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--hairline);border-radius:3px}
.container{max-width:1200px;margin:0 auto;padding:0 24px}
a{color:var(--primary);text-decoration:none}
a:hover{text-decoration:underline}
/* ── Header ── */
header{position:fixed;top:0;left:0;right:0;z-index:1000;background:var(--canvas);border-bottom:1px solid var(--hairline);transition:all .3s}
header.scrolled{box-shadow:0 1px 3px rgba(20,20,19,.06)}
.header-inner{max-width:1200px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:64px;padding:0 24px}
.logo{display:flex;align-items:center;gap:8px;text-decoration:none;color:var(--ink)}
.logo-mark{font-family:var(--font-display);font-size:1.3rem;font-weight:600;letter-spacing:-.02em}
.logo-mark span{color:var(--primary)}
nav{display:flex;align-items:center;gap:4px}
nav a{color:var(--muted);text-decoration:none;font-size:.875rem;font-weight:500;padding:6px 14px;border-radius:8px;transition:all .15s}
nav a:hover{color:var(--ink);background:var(--surface-card);text-decoration:none}
.nav-cta{margin-left:8px;background:var(--primary)!important;color:var(--on-primary)!important;padding:8px 18px!important;border-radius:8px!important;font-weight:500!important}
.nav-cta:hover{background:var(--primary-active)!important}
/* ── Hero ── */
.hero{padding:140px 0 80px;max-width:1200px;margin:0 auto;padding-left:24px;padding-right:24px;display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center}
.hero-badge{display:inline-flex;align-items:center;gap:8px;background:var(--surface-card);border:1px solid var(--hairline);padding:6px 16px;border-radius:9999px;font-size:.75rem;font-weight:500;color:var(--muted);margin-bottom:20px;letter-spacing:.5px;text-transform:uppercase}
.hero-badge .dot{width:6px;height:6px;border-radius:50%;background:var(--success);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.hero h1{font-family:var(--font-display);font-size:clamp(2.5rem,5vw,3.5rem);font-weight:400;line-height:1.1;letter-spacing:-.02em;color:var(--ink);margin-bottom:16px}
.hero-sub{color:var(--body);font-size:1.05rem;max-width:480px;line-height:1.7;margin-bottom:32px}
.hero-actions{display:flex;gap:12px;flex-wrap:wrap}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border-radius:8px;font-size:.875rem;font-weight:500;text-decoration:none;border:none;cursor:pointer;transition:all .15s;font-family:var(--font-body)}
.btn svg{width:16px;height:16px}
.btn-primary{background:var(--primary);color:var(--on-primary)}
.btn-primary:hover{background:var(--primary-active);text-decoration:none}
.btn-secondary{background:var(--canvas);border:1px solid var(--hairline);color:var(--ink)}
.btn-secondary:hover{background:var(--surface-card);text-decoration:none}
/* ── Console ── */
.console-wrap{background:var(--surface-dark);border-radius:12px;overflow:hidden;box-shadow:0 24px 80px rgba(20,20,19,.15)}
.console-bar{display:flex;align-items:center;gap:8px;padding:14px 18px;background:var(--surface-dark-elevated);border-bottom:1px solid rgba(255,255,255,.06)}
.console-dot{width:10px;height:10px;border-radius:50%}
.console-dot:nth-child(1){background:#ff5f57}
.console-dot:nth-child(2){background:#febc2e}
.console-dot:nth-child(3){background:#28c840}
.console-title{margin-left:12px;font-size:.75rem;color:var(--on-dark-soft);font-family:var(--font-code)}
.console-body{display:flex;min-height:180px}
.console-input{flex:1;padding:18px;font-family:var(--font-code);font-size:.8rem;line-height:1.8;color:var(--on-dark);border:none;background:transparent;resize:none;outline:none}
.console-input::placeholder{color:var(--on-dark-soft)}
.console-output{flex:1;padding:18px;border-left:1px solid rgba(255,255,255,.06);font-family:var(--font-code);font-size:.78rem;line-height:1.8;color:var(--success);overflow:auto;max-height:300px;white-space:pre-wrap}
.console-actions{display:flex;gap:8px;padding:12px 18px;border-top:1px solid rgba(255,255,255,.06);background:var(--surface-dark-elevated)}
.console-btn{padding:6px 14px;border-radius:6px;border:1px solid rgba(255,255,255,.08);background:transparent;color:var(--on-dark-soft);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s;font-family:var(--font-code)}
.console-btn:hover{background:rgba(255,255,255,.05);color:var(--on-dark)}
.console-btn.active{background:rgba(204,120,92,.15);color:var(--primary);border-color:rgba(204,120,92,.3)}
.console-send{margin-left:auto;padding:6px 16px;border-radius:6px;border:none;background:var(--primary);color:var(--on-primary);font-size:.75rem;font-weight:500;cursor:pointer;transition:all .15s;font-family:var(--font-code)}
.console-send:hover{background:var(--primary-active)}
/* ── Stats ── */
.stats{padding:60px 0;border-top:1px solid var(--hairline);border-bottom:1px solid var(--hairline);background:var(--surface-soft)}
.stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px;text-align:center}
.stat-value{font-family:var(--font-display);font-size:2.5rem;font-weight:400;letter-spacing:-.02em;color:var(--ink)}
.stat-label{font-size:.8rem;color:var(--muted);margin-top:4px;font-weight:500}
/* ── Section ── */
.section{padding:96px 0}
.section-label{display:inline-flex;align-items:center;gap:6px;font-size:.7rem;text-transform:uppercase;letter-spacing:1.5px;color:var(--primary);font-weight:600;margin-bottom:12px}
.section-title{font-family:var(--font-display);font-size:clamp(1.8rem,3vw,2.4rem);font-weight:400;letter-spacing:-.01em;margin-bottom:10px;line-height:1.2;color:var(--ink)}
.section-desc{color:var(--body);font-size:1rem;max-width:520px;line-height:1.7;margin-bottom:40px}
/* ── Features Grid ── */
.features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
.feature-card{background:var(--surface-card);border:1px solid var(--hairline);border-radius:12px;padding:32px;transition:all .2s}
.feature-card:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(20,20,19,.06)}
.feature-icon{width:44px;height:44px;border-radius:8px;display:flex;align-items:center;justify-content:center;margin-bottom:18px;background:var(--canvas);border:1px solid var(--hairline)}
.feature-icon svg{width:20px;height:20px;color:var(--primary)}
.feature-title{font-family:var(--font-display);font-size:1.1rem;font-weight:500;margin-bottom:8px;color:var(--ink)}
.feature-desc{color:var(--body);font-size:.875rem;line-height:1.7}
/* ── Endpoint Explorer ── */
.explorer{display:grid;grid-template-columns:240px 1fr;gap:1px;background:var(--hairline);border-radius:12px;overflow:hidden;border:1px solid var(--hairline)}
.explorer-nav{background:var(--surface-dark);padding:8px}
.explorer-nav-btn{display:flex;align-items:center;gap:8px;width:100%;padding:10px 14px;border:none;background:transparent;color:var(--on-dark-soft);font-size:.8rem;font-weight:500;border-radius:8px;cursor:pointer;transition:all .15s;text-align:left;font-family:var(--font-body)}
.explorer-nav-btn:hover{background:rgba(255,255,255,.05);color:var(--on-dark)}
.explorer-nav-btn.active{background:rgba(204,120,92,.15);color:var(--primary)}
.explorer-nav-btn svg{width:16px;height:16px;flex-shrink:0}
.explorer-panel{background:var(--surface-dark);padding:24px;min-height:400px}
.explorer-panel-title{font-family:var(--font-display);font-size:1.2rem;font-weight:500;margin-bottom:4px;color:var(--on-dark)}
.explorer-panel-desc{color:var(--on-dark-soft);font-size:.84rem;margin-bottom:20px;line-height:1.6}
.explorer-method{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:6px;font-size:.7rem;font-weight:700;font-family:var(--font-code);letter-spacing:.04em;margin-bottom:12px;background:rgba(93,184,114,.15);color:var(--success)}
.explorer-path{font-family:var(--font-code);font-size:.85rem;color:var(--on-dark);margin-bottom:16px;padding:12px 16px;background:var(--surface-dark-soft);border-radius:8px;border:1px solid rgba(255,255,255,.06)}
.explorer-params{margin-bottom:16px}
.explorer-params-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:var(--on-dark-soft);font-weight:600;margin-bottom:8px}
.param-row{display:grid;grid-template-columns:120px 80px 1fr;gap:12px;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.03);font-size:.8rem}
.param-name{font-family:var(--font-code);color:var(--accent-amber);font-weight:500}
.param-type{font-family:var(--font-code);color:var(--accent-teal);font-size:.75rem}
.param-desc{color:var(--on-dark-soft)}
.explorer-code{background:var(--surface-dark-soft);border-radius:8px;padding:16px;font-family:var(--font-code);font-size:.78rem;line-height:1.8;color:var(--on-dark-soft);overflow-x:auto;border:1px solid rgba(255,255,255,.06)}
/* ── Code Tabs ── */
.code-tabs{display:flex;gap:2px;background:var(--surface-dark-soft);border-radius:8px;padding:3px;margin-bottom:12px;width:fit-content}
.code-tab{padding:6px 14px;border-radius:6px;border:none;background:transparent;color:var(--on-dark-soft);font-size:.72rem;font-weight:600;cursor:pointer;transition:all .15s;font-family:var(--font-code)}
.code-tab.active{background:rgba(204,120,92,.15);color:var(--primary)}
.code-tab:hover:not(.active){color:var(--on-dark)}
/* ── Playground ── */
.playground-card{background:var(--surface-card);border:1px solid var(--hairline);border-radius:12px;overflow:hidden}
.playground-header{padding:20px 24px;border-bottom:1px solid var(--hairline);display:flex;align-items:center;gap:10px}
.playground-header svg{width:18px;height:18px;color:var(--primary)}
.playground-header h3{font-family:var(--font-display);font-size:1.1rem;font-weight:500;color:var(--ink)}
.playground-body{padding:24px}
.playground-inputs{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.playground-inputs input,.playground-inputs select{flex:1;min-width:200px;padding:10px 14px;border-radius:8px;border:1px solid var(--hairline);background:var(--canvas);color:var(--ink);font-size:.85rem;font-family:var(--font-code);outline:none;transition:border-color .15s}
.playground-inputs input:focus,.playground-inputs select:focus{border-color:var(--primary)}
.playground-inputs input::placeholder{color:var(--muted-soft)}
.playground-inputs select option{background:var(--canvas)}
.playground-output{background:var(--surface-dark);border:1px solid rgba(255,255,255,.06);border-radius:8px;padding:16px;font-family:var(--font-code);font-size:.78rem;line-height:1.7;color:var(--success);max-height:400px;overflow:auto;white-space:pre-wrap;display:none}
.playground-output.visible{display:block}
/* ── Marquee ── */
.marquee{padding:40px 0;overflow:hidden;border-top:1px solid var(--hairline);border-bottom:1px solid var(--hairline);background:var(--surface-soft)}
.marquee-track{display:flex;gap:40px;animation:marquee 30s linear infinite;width:max-content}
.marquee-item{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:.8rem;font-weight:500;white-space:nowrap}
.marquee-item svg{width:16px;height:16px;opacity:.5}
@keyframes marquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
/* ── CTA ── */
.cta{text-align:center;padding:96px 0}
.cta-box{background:var(--primary);border-radius:16px;padding:64px 40px;position:relative;overflow:hidden}
.cta-title{font-family:var(--font-display);font-size:clamp(1.8rem,3vw,2.4rem);font-weight:400;letter-spacing:-.01em;margin-bottom:12px;color:var(--on-primary)}
.cta-desc{color:rgba(255,255,255,.85);font-size:1rem;margin-bottom:28px}
.cta-actions{display:flex;gap:12px;justify-content:center}
.btn-on-coral{background:var(--canvas);color:var(--ink);border:1px solid var(--hairline)}
.btn-on-coral:hover{background:var(--surface-card);text-decoration:none}
/* ── Footer ── */
footer{background:var(--surface-dark);color:var(--on-dark-soft);padding:64px 0 40px}
.footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:40px;margin-bottom:40px}
.footer-brand .logo{margin-bottom:12px}
.footer-brand .logo .logo-mark{color:var(--on-dark)}
.footer-brand p{color:var(--on-dark-soft);font-size:.84rem;line-height:1.7;max-width:280px}
.footer-col h4{font-size:.7rem;text-transform:uppercase;letter-spacing:1.5px;color:var(--on-dark-soft);font-weight:600;margin-bottom:16px}
.footer-col a{display:block;color:var(--on-dark-soft);text-decoration:none;font-size:.84rem;padding:4px 0;transition:color .15s}
.footer-col a:hover{color:var(--on-dark)}
.footer-bottom{display:flex;align-items:center;justify-content:space-between;padding-top:24px;border-top:1px solid rgba(255,255,255,.06);color:var(--on-dark-soft);font-size:.75rem}
.footer-social{display:flex;gap:12px}
.footer-social a{color:var(--on-dark-soft);transition:color .15s}
.footer-social a:hover{color:var(--on-dark)}
.footer-social svg{width:18px;height:18px}
/* ── Scroll Reveal ── */
.reveal{opacity:0;transform:translateY(20px);transition:all .6s cubic-bezier(.16,1,.3,1)}
.reveal.visible{opacity:1;transform:translateY(0)}
.reveal-delay-1{transition-delay:.1s}
.reveal-delay-2{transition-delay:.2s}
.reveal-delay-3{transition-delay:.3s}
/* ── Mobile ── */
@media(max-width:900px){
.hero{grid-template-columns:1fr;gap:40px}
.features-grid{grid-template-columns:repeat(2,1fr)}
.explorer{grid-template-columns:1fr}
.stats-grid{grid-template-columns:repeat(2,1fr)}
.footer-grid{grid-template-columns:1fr 1fr}
}
@media(max-width:640px){
.hero{padding:100px 0 60px}
.hero h1{font-size:2rem}
.features-grid{grid-template-columns:1fr}
.console-body{flex-direction:column}
.console-output{border-left:none;border-top:1px solid rgba(255,255,255,.06)}
.stats-grid{grid-template-columns:repeat(2,1fr);gap:16px}
.footer-grid{grid-template-columns:1fr}
nav{display:none}
}
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebAPI",
"name": "AniKotoAPI",
"description": "Free REST API for anime data. Browse, search, stream episodes. 33+ endpoints, no API key required.",
"url": "https://anikototvapi.vercel.app",
"image": "https://anikototvapi.vercel.app/share.png",
"documentation": "https://github.com/Shineii86/AniKotoAPI",
"provider": {
"@type": "Organization",
"name": "AniKotoAPI",
"url": "https://github.com/Shineii86/AniKotoAPI"
},
"category": "Anime Data API",
"endpoint": "https://anikototvapi.vercel.app/api",
"programmingModel": "REST"
}
</script>
</head>
<body>
<div class="wrap">
<header id="header">
<div class="header-inner">
<a href="/" class="logo">
<span class="logo-mark"><span>*</span> AniKotoAPI</span>
</a>
<nav>
<a href="#features">Features</a>
<a href="#endpoints">Endpoints</a>
<a href="#playground">Playground</a>
<a href="https://github.com/Shineii86/AniKotoAPI" target="_blank" rel="noopener noreferrer">GitHub</a>
<a href="https://anikototvapi.vercel.app/api" target="_blank" class="nav-cta">Open API</a>
</nav>
</div>
</header>
<section class="hero">
<div>
<div class="hero-badge"><span class="dot"></span> Free &middot; No API key &middot; Open source</div>
<h1>The anime data API for your next project</h1>
<p class="hero-sub">Browse, search, and stream anime data from AniKoto. Real-time scraping with 33+ endpoints, in-memory caching, and zero configuration.</p>
<div class="hero-actions">
<a href="#playground" class="btn btn-primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
Try It Live
</a>
<a href="https://github.com/Shineii86/AniKotoAPI" target="_blank" class="btn btn-secondary">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
Star on GitHub
</a>
</div>
</div>
<div class="console-wrap">
<div class="console-bar">
<span class="console-dot"></span><span class="console-dot"></span><span class="console-dot"></span>
<span class="console-title">anikototvapi.vercel.app/api</span>
</div>
<div class="console-body">
<textarea class="console-input" id="consoleInput" rows="6" spellcheck="false">// Fetch trending anime
fetch("https://anikototvapi.vercel.app/api")
.then(r => r.json())
.then(data => {
console.log(data.trending);
});</textarea>
<div class="console-output" id="consoleOutput"><span style="color:var(--on-dark-soft)">// Click "Run" to see the response</span></div>
</div>
<div class="console-actions">
<button class="console-btn active" onclick="setEndpoint('home', event)">Home</button>
<button class="console-btn" onclick="setEndpoint('search', event)">Search</button>
<button class="console-btn" onclick="setEndpoint('stream', event)">Stream</button>
<button class="console-btn" onclick="setEndpoint('random', event)">Random</button>
<button class="console-btn" onclick="setEndpoint('schedule', event)">Schedule</button>
<button class="console-send" onclick="runConsole()">Run</button>
</div>
</div>
</section>
<section class="stats">
<div class="container">
<div class="stats-grid">
<div class="reveal"><div class="stat-value" data-count="33">0</div><div class="stat-label">API Endpoints</div></div>
<div class="reveal reveal-delay-1"><div class="stat-value" data-count="0">0</div><div class="stat-label">API Keys Required</div></div>
<div class="reveal reveal-delay-2"><div class="stat-value" data-count="40">0</div><div class="stat-label">Anime Genres</div></div>
<div class="reveal reveal-delay-3"><div class="stat-value">Live</div><div class="stat-label">Real-time Data</div></div>
</div>
</div>
</section>
<div class="marquee">
<div class="marquee-track">
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 5-min cache TTL</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg> Zero config</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> No auth needed</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8m-4-4v4"/></svg> Vercel deployed</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg> Cheerio scraper</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg> REST JSON</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/></svg> CORS enabled</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg> Open source</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 5-min cache TTL</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg> Zero config</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> No auth needed</div>
<div class="marquee-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8m-4-4v4"/></svg> Vercel deployed</div>
</div>
</div>
<section class="section" id="features">
<div class="container">
<div class="reveal">
<div class="section-label">Features</div>
<h2 class="section-title">Built for developers</h2>
<p class="section-desc">Everything you need to build an anime website, mobile app, or Discord bot. No API keys, no rate limits, no hassle.</p>
</div>
<div class="features-grid">
<div class="feature-card reveal">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg></div>
<div class="feature-title">Full-text Search</div>
<div class="feature-desc">Search by keyword, genre, type, status. Get autocomplete suggestions and paginated results.</div>
</div>
<div class="feature-card reveal reveal-delay-1">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="2.18"/><path d="M7 2v20M17 2v20M2 12h20M2 7h5M2 17h5M17 7h5M17 17h5"/></svg></div>
<div class="feature-title">Episode Streaming</div>
<div class="feature-desc">Retrieve streaming servers, source links, and m3u8 URLs for any episode via AJAX endpoints.</div>
</div>
<div class="feature-card reveal reveal-delay-2">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M16 13H8"/><path d="M16 17H8"/><path d="M10 9H8"/></svg></div>
<div class="feature-title">Rich Metadata</div>
<div class="feature-desc">Synopsis, MAL score, genres, characters, relations, recommendations, and more.</div>
</div>
<div class="feature-card reveal">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></div>
<div class="feature-title">Schedule & Calendar</div>
<div class="feature-desc">View what's airing today or fetch the full weekly broadcast schedule.</div>
</div>
<div class="feature-card reveal reveal-delay-1">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg></div>
<div class="feature-title">Advanced Filters</div>
<div class="feature-desc">Filter by genre, type, status, season, sort order. Multi-select support.</div>
</div>
<div class="feature-card reveal reveal-delay-2">
<div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg></div>
<div class="feature-title">Fast & Cached</div>
<div class="feature-desc">In-memory cache with configurable TTL for snappy responses and reduced scraping overhead.</div>
</div>
</div>
</div>
</section>
<section class="section" id="endpoints" style="background:var(--surface-soft);border-top:1px solid var(--hairline);border-bottom:1px solid var(--hairline)">
<div class="container">
<div class="reveal">
<div class="section-label">API Reference</div>
<h2 class="section-title">Endpoints</h2>
<p class="section-desc">All endpoints return JSON. Base URL: <code style="color:var(--primary);background:rgba(204,120,92,.1);padding:2px 8px;border-radius:4px;font-family:var(--font-code);font-size:.85rem">https://anikototvapi.vercel.app/api</code></p>
</div>
<div class="explorer reveal" id="explorer">
<div class="explorer-nav" id="explorerNav"></div>
<div class="explorer-panel" id="explorerPanel"></div>
</div>
</div>
</section>
<section class="section" id="playground">
<div class="container">
<div class="reveal">
<div class="section-label">Interactive</div>
<h2 class="section-title">Playground</h2>
<p class="section-desc">Test endpoints directly in your browser. No signup required.</p>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px">
<div class="playground-card reveal">
<div class="playground-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
<h3>Search Anime</h3>
</div>
<div class="playground-body">
<div class="playground-inputs">
<input id="pgSearch" type="text" placeholder="e.g. naruto, one piece" aria-label="Search anime" />
<button class="btn btn-primary" onclick="pgRun('search')" style="padding:10px 20px">Search</button>
</div>
<div class="playground-output" id="pgSearchOut"></div>
</div>
</div>
<div class="playground-card reveal reveal-delay-1">
<div class="playground-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
<h3>Stream Servers</h3>
</div>
<div class="playground-body">
<div class="playground-inputs">
<input id="pgStream" type="text" placeholder="Episode ID" />
<button class="btn btn-primary" onclick="pgRun('stream')" style="padding:10px 20px">Get Servers</button>
</div>
<div class="playground-output" id="pgStreamOut"></div>
</div>
</div>
<div class="playground-card reveal reveal-delay-2">
<div class="playground-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="M3.27 6.96L12 12.01l8.73-5.05M12 22.08V12"/></svg>
<h3>Random Anime</h3>
</div>
<div class="playground-body">
<div class="playground-inputs">
<button class="btn btn-primary" onclick="pgRun('random')" style="padding:10px 20px">Get Random</button>
</div>
<div class="playground-output" id="pgRandomOut"></div>
</div>
</div>
<div class="playground-card reveal reveal-delay-3">
<div class="playground-header">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
<h3>Schedule</h3>
</div>
<div class="playground-body">
<div class="playground-inputs">
<input id="pgSchedule" type="date" aria-label="Schedule date" />
<button class="btn btn-primary" onclick="pgRun('schedule')" style="padding:10px 20px">Get Schedule</button>
</div>
<div class="playground-output" id="pgScheduleOut"></div>
</div>
</div>
</div>
</div>
</section>
<section class="cta">
<div class="container">
<div class="cta-box reveal">
<h2 class="cta-title">Start building with AniKotoAPI</h2>
<p class="cta-desc">Free, open source, no API key required. Deploy your own instance or use ours.</p>
<div class="cta-actions">
<a href="https://anikototvapi.vercel.app/api" target="_blank" class="btn btn-on-coral">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
View API
</a>
<a href="https://github.com/Shineii86/AniKotoAPI" target="_blank" class="btn btn-on-coral">
<svg viewBox="0 0 24 24" fill="currentColor" style="width:16px;height:16px"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
Source Code
</a>
</div>
</div>
</div>
</section>
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a href="/" class="logo">
<span class="logo-mark"><span>*</span> AniKotoAPI</span>
</a>
<p>Free REST API for anime data. Browse, search, and stream from AniKoto. Built with Node.js and deployed on Vercel.</p>
</div>
<div class="footer-col">
<h4>API</h4>
<a href="/api">Home</a>
<a href="/api/search?keyword=naruto">Search</a>
<a href="/api/random">Random</a>
<a href="/api/schedule">Schedule</a>
<a href="/api/top-ten">Top 10</a>
</div>
<div class="footer-col">
<h4>Resources</h4>
<a href="https://github.com/Shineii86/AniKotoAPI" target="_blank" rel="noopener noreferrer">GitHub</a>
<a href="https://github.com/Shineii86/AniKotoAPI#readme" target="_blank" rel="noopener noreferrer">Documentation</a>
<a href="https://github.com/Shineii86/AniKotoAPI/blob/main/CHANGELOG.md" target="_blank" rel="noopener noreferrer">Changelog</a>
<a href="/api" target="_blank" rel="noopener noreferrer">API Status</a>
</div>
<div class="footer-col">
<h4>Legal</h4>
<a href="/privacy">Privacy Policy</a>
<a href="/tos">Terms of Service</a>
</div>
</div>
<div class="footer-bottom">
<span>&copy; 2026 AniKotoAPI. Educational use only.</span>
<div class="footer-social">
<a href="https://github.com/Shineii86/AniKotoAPI" target="_blank" aria-label="GitHub">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<script>
const BASE = 'https://anikototvapi.vercel.app/api';
const endpoints = {
home: { url: '/', label: 'Home' },
search: { url: '/search?keyword=naruto', label: 'Search' },
stream: { url: '/stream?id=naruto-ep-1', label: 'Stream' },
random: { url: '/random', label: 'Random' },
schedule: { url: '/schedule', label: 'Schedule' }
};
function setEndpoint(key, event) {
const ep = endpoints[key];
document.getElementById('consoleInput').value = `// ${ep.label}\nfetch("${BASE}${ep.url}")\n .then(r => r.json())\n .then(data => console.log(data));`;
document.querySelectorAll('.console-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
async function runConsole() {
const out = document.getElementById('consoleOutput');
out.innerHTML = '<span style="color:var(--accent-amber)">Loading...</span>';
const input = document.getElementById('consoleInput').value;
const urlMatch = input.match(/fetch\("([^"]+)"\)/);
if (!urlMatch) { out.innerHTML = '<span style="color:var(--error)">No fetch URL found</span>'; return; }
try {
const res = await fetch(urlMatch[1]);
const data = await res.json();
out.innerHTML = syntaxHighlight(JSON.stringify(data, null, 2));
out.scrollTop = out.scrollHeight;
} catch (e) {
out.innerHTML = `<span style="color:var(--error)">Error: ${e.message}</span>`;
}
}
function syntaxHighlight(json) {
return json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"([^"]+)"(?=\s*:)/g, '<span style="color:var(--accent-amber)">"$1"</span>')
.replace(/: "([^"]*)"/g, ': <span style="color:var(--accent-teal)">"$1"</span>')
.replace(/: (\d+)/g, ': <span style="color:var(--primary)">$1</span>')
.replace(/: (true|false|null)/g, ': <span style="color:var(--on-dark-soft)">$1</span>');
}
const header = document.getElementById('header');
window.addEventListener('scroll', () => {
header.classList.toggle('scrolled', window.scrollY > 20);
});
const reveals = document.querySelectorAll('.reveal');
const revealObserver = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); revealObserver.unobserve(e.target); } });
}, { threshold: .1, rootMargin: '0px 0px -40px 0px' });
reveals.forEach(el => revealObserver.observe(el));
document.querySelectorAll('[data-count]').forEach(el => {
const target = parseInt(el.dataset.count);
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting) {
let current = 0;
const step = Math.ceil(target / 30);
const timer = setInterval(() => {
current += step;
if (current >= target) { current = target; clearInterval(timer); }
el.textContent = current;
}, 30);
obs.unobserve(el);
}
}, { threshold: .5 });
obs.observe(el);
});
const explorerData = [
{ id: 'home', title: 'Home Info', desc: 'Spotlight, trending, popular, top-airing, genres, and more.', path: '/', params: [], code: { curl: 'curl https://anikototvapi.vercel.app/api', js: 'const res = await fetch("https://anikototvapi.vercel.app/api");\nconst data = await res.json();\nconsole.log(data.trending);', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api")\nprint(r.json()["trending"])' } },
{ id: 'search', title: 'Search', desc: 'Search anime by keyword with pagination.', path: '/search?keyword={q}&page={n}', params: [{ name: 'keyword', type: 'string', desc: 'Search query' }, { name: 'page', type: 'number', desc: 'Page (default 1)' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/search?keyword=naruto"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/search?keyword=naruto");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/search", params={"keyword": "naruto"})\nprint(r.json())' } },
{ id: 'info', title: 'Anime Info', desc: 'Detailed info: synopsis, genres, relations, recommendations.', path: '/info?id={slug}', params: [{ name: 'id', type: 'string', desc: 'Anime slug' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/info?id=naruto"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/info?id=naruto");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/info", params={"id": "naruto"})\nprint(r.json())' } },
{ id: 'episodes', title: 'Episodes', desc: 'Episode list loaded via AJAX.', path: '/episodes/{animeId}', params: [{ name: 'animeId', type: 'number', desc: 'Anime ID' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/episodes/958"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/episodes/958");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/episodes/958")\nprint(r.json())' } },
{ id: 'stream', title: 'Stream Servers', desc: 'Streaming servers for an episode.', path: '/stream?id={linkId}', params: [{ name: 'id', type: 'string', desc: 'Link ID from servers' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/stream?id=MTF1d..."', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/stream?id=MTF1d...");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/stream", params={"id": "MTF1d..."})\nprint(r.json())' } },
{ id: 'servers', title: 'Servers', desc: 'Resolve streaming server links.', path: '/servers?ids={serverIds}', params: [{ name: 'ids', type: 'string', desc: 'Server IDs from episodes' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/servers?ids=SlNVT..."', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/servers?ids=SlNVT...");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/servers", params={"ids": "SlNVT..."})\nprint(r.json())' } },
{ id: 'schedule', title: 'Schedule', desc: 'Anime schedule for a specific date.', path: '/schedule?date={YYYY-MM-DD}', params: [{ name: 'date', type: 'string', desc: 'Date (default today)' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/schedule"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/schedule");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/schedule")\nprint(r.json())' } },
{ id: 'topten', title: 'Top 10', desc: 'Top 10 anime (today, week, month).', path: '/top-ten', params: [], code: { curl: 'curl "https://anikototvapi.vercel.app/api/top-ten"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/top-ten");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/top-ten")\nprint(r.json())' } },
{ id: 'random', title: 'Random', desc: 'Random anime info.', path: '/random', params: [], code: { curl: 'curl "https://anikototvapi.vercel.app/api/random"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/random");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/random")\nprint(r.json())' } },
{ id: 'filter', title: 'Filter', desc: 'Filter by genre, type, status, season.', path: '/filter?keyword={q}&genre[]={g}', params: [{ name: 'keyword', type: 'string', desc: 'Search keyword (required)' }, { name: 'genre[]', type: 'number', desc: 'Genre IDs' }, { name: 'type[]', type: 'number', desc: 'Type IDs' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/filter?keyword=&genre[]=1&genre[]=4"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/filter?keyword=&genre[]=1");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/filter", params={"keyword": "", "genre[]": 1})\nprint(r.json())' } },
{ id: 'popular', title: 'Most Popular', desc: 'Most popular anime list.', path: '/most-popular?page={n}', params: [{ name: 'page', type: 'number', desc: 'Page (default 1)' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/most-popular"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/most-popular");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/most-popular")\nprint(r.json())' } },
{ id: 'genre', title: 'Genre', desc: 'Anime by genre.', path: '/genre/{name}', params: [{ name: 'name', type: 'string', desc: 'Genre slug (action, comedy, etc.)' }], code: { curl: 'curl "https://anikototvapi.vercel.app/api/genre/action"', js: 'const res = await fetch("https://anikototvapi.vercel.app/api/genre/action");\nconst data = await res.json();', python: 'import requests\nr = requests.get("https://anikototvapi.vercel.app/api/genre/action")\nprint(r.json())' } },
];
const navEl = document.getElementById('explorerNav');
const panelEl = document.getElementById('explorerPanel');
let activeLang = 'curl';
function renderExplorer(ep) {
const paramsHtml = ep.params.map(p => `<div class="param-row"><span class="param-name">${p.name}</span><span class="param-type">${p.type}</span><span class="param-desc">${p.desc}</span></div>`).join('');
panelEl.innerHTML = `
<div class="explorer-method method-get">GET</div>
<div class="explorer-panel-title">${ep.title}</div>
<div class="explorer-panel-desc">${ep.desc}</div>
<div class="explorer-path">${ep.path}</div>
${ep.params.length ? `<div class="explorer-params"><div class="explorer-params-title">Parameters</div>${paramsHtml}</div>` : ''}
<div class="code-tabs">
<button class="code-tab ${activeLang==='curl'?'active':''}" onclick="switchLang('curl',this)">cURL</button>
<button class="code-tab ${activeLang==='js'?'active':''}" onclick="switchLang('js',this)">JavaScript</button>
<button class="code-tab ${activeLang==='python'?'active':''}" onclick="switchLang('python',this)">Python</button>
</div>
<div class="explorer-code" id="explorerCode">${ep.code[activeLang]}</div>`;
}
function switchLang(lang, btn) {
activeLang = lang;
document.querySelectorAll('.code-tab').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const activeEp = explorerData.find(e => e.id === navEl.querySelector('.active')?.dataset.id) || explorerData[0];
document.getElementById('explorerCode').textContent = activeEp.code[lang];
}
explorerData.forEach((ep, i) => {
const btn = document.createElement('button');
btn.className = `explorer-nav-btn ${i===0?'active':''}`;
btn.dataset.id = ep.id;
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>${ep.title}`;
btn.onclick = () => {
navEl.querySelectorAll('.explorer-nav-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
renderExplorer(ep);
};
navEl.appendChild(btn);
});
renderExplorer(explorerData[0]);
async function pgRun(type) {
let url, outEl;
if (type === 'search') {
const q = document.getElementById('pgSearch').value.trim();
if (!q) return;
url = `${BASE}/search?keyword=${encodeURIComponent(q)}`;
outEl = document.getElementById('pgSearchOut');
} else if (type === 'stream') {
const id = document.getElementById('pgStream').value.trim();
if (!id) return;
url = `${BASE}/stream?id=${encodeURIComponent(id)}`;
outEl = document.getElementById('pgStreamOut');
} else if (type === 'random') {
url = `${BASE}/random`;
outEl = document.getElementById('pgRandomOut');
} else if (type === 'schedule') {
const d = document.getElementById('pgSchedule').value;
url = d ? `${BASE}/schedule?date=${d}` : `${BASE}/schedule`;
outEl = document.getElementById('pgScheduleOut');
}
outEl.classList.add('visible');
outEl.innerHTML = '<span style="color:var(--accent-amber)">Loading...</span>';
try {
const res = await fetch(url);
const data = await res.json();
outEl.innerHTML = syntaxHighlight(JSON.stringify(data, null, 2));
} catch (e) {
outEl.innerHTML = `<span style="color:var(--error)">Error: ${e.message}</span>`;
}
}
document.getElementById('pgSearch')?.addEventListener('keydown', e => { if (e.key === 'Enter') pgRun('search'); });
document.getElementById('pgStream')?.addEventListener('keydown', e => { if (e.key === 'Enter') pgRun('stream'); });
</script>
</body>
</html>