AniKino API

API Documentation

Embed movies, TV shows, and anime on your own site through AniKino - one iframe, one URL, pulling from our MVP, KaF, Neko, RU MV, and Kino sources under the hood so you don't have to juggle providers yourself.

Everything on this page is live and working - every example is a real, tested URL, and so are player postMessage events, startAt/resume-playback, and autoplay/autonext (see their own sections further down). Nothing left marked TBD.

Introduction

Drop a single <iframe> into your own site and get a fully working AniKino player back - no API key, no sign-up. Every embed resolves the real stream server-side and serves it through AniKino's own Plyr-based player, not a nested third-party iframe - the actual provider CDN URL is never exposed to your page or your visitors.

Movie TV Show Anime
<iframe
  src="https://your-domain.example/embed/movie/{id}"
  width="100%" height="560"
  frameborder="0" allowfullscreen
></iframe>
<iframe
  src="https://your-domain.example/embed/tv/{id}/{season}/{episode}"
  width="100%" height="560"
  frameborder="0" allowfullscreen
></iframe>
<iframe
  src="https://your-domain.example/embed/anime/{id}/{episode}?season=1&audio=dub"
  width="100%" height="560"
  frameborder="0" allowfullscreen
></iframe>
Every {id} below accepts either a numeric TMDB id or an IMDB id (tt1300854-style) - both are resolved automatically, no need to tell us which one you're sending.
/embed/* is rate-limited to 40 requests / 5 minutes per IP - plenty for a real viewer loading/reloading a player, not enough for bulk scraping. It's also the only part of AniKino's API that's deliberately reachable cross-origin; everything else is locked to our own frontend.

Providers

Each embed type has its own default source and an alternate you can force with ?server=. Anime and Movie/TV don't share providers - the same server=ru value means a different backend depending on which embed you're calling, both labeled RU MV on the frontend since they're both "the Russian-dub source" for their respective content type.

The dot on each card below is that provider's live status - checked server-side on a timer against a fixed, known-good title (not on page load), so it reflects whether the provider itself is actually reachable right now, not just this browser's own network.

MVP anime, default

Broad anime coverage, usually the fastest to resolve.

server=mega
Checking...

KaF anime

Broad anime coverage, but often the slowest of the four to resolve - and doesn't always have a dub (or the title at all).

server=kaa
Checking...

Neko anime

Lightweight, quick-resolving anime source - falls back to MVP automatically on titles it doesn't have.

server=neko
Checking...

RU MV anime

Russian-dub anime source. Only serves a title when we're confident in the match - fails clearly rather than guessing wrong.

server=ru
Checking...

Kino movie/TV, default

Our primary movie/TV source - broad English-language coverage.

server=kino
Checking...

T1M movie/TV

English movie/TV source with its own large multi-language subtitle set - good alternate when Kino doesn't have a title.

server=t1m
Checking...

VixSrc movie/TV

English-preferred HLS movie/TV source with a plain-HTTP resolve - extra alternate on the player page (not available in /embed).

player only
Checking...

RU MV movie/TV

Russian-dub movie/TV source - single audio track, no subtitles of its own.

server=ru
Checking...
GET /embed/movie/{id}

Movie Embed

Embed a movie player. Resolves through Kino (default), T1M, or RU MV.

ParameterRequiredDescription
{id}YesTMDB movie id or IMDB id (tt...)
serverOptionalkino (default), t1m, or ru - see Providers
startAtOptionalSeconds into the video to start playback at - see Resume Playback
autoplayOptional1 to start playback automatically once the stream loads
HTML URL
<iframe src="https://your-domain.example/embed/movie/{id}" width="100%" height="560" allowfullscreen></iframe>
# Deadpool 2, default (Kino)/embed/movie/293660# same movie, by IMDB id/embed/movie/tt4425200# forced T1M/embed/movie/293660?server=t1m# forced RU MV/embed/movie/293660?server=ru# autoplay on load, resume from 2 minutes in/embed/movie/293660?autoplay=1&startAt=120
GET /embed/tv/{id}/{season}/{episode}

TV Show Embed

Embed a specific episode. Resolves through Kino (default), T1M, or RU MV.

ParameterRequiredDescription
{id}YesTMDB TV id or IMDB id (tt...)
{season}YesSeason number
{episode}YesEpisode number within that season
serverOptionalkino (default), t1m, or ru - see Providers
startAtOptionalSeconds into the video to start playback at - see Resume Playback
autoplayOptional1 to start playback automatically once the stream loads
autonextOptional1 to load episode + 1 (same season) automatically when this one ends
HTML URL
<iframe src="https://your-domain.example/embed/tv/{id}/{season}/{episode}" width="100%" height="560" allowfullscreen></iframe>
# Breaking Bad S1E1, default (Kino)/embed/tv/1396/1/1# forced T1M/embed/tv/1396/1/1?server=t1m# forced RU MV/embed/tv/1396/1/1?server=ru# binge mode - autoplay + auto-advance to S1E2, S1E3... on end/embed/tv/1396/1/1?autoplay=1&autonext=1
GET /embed/anime/{id}/{episode}

Anime Embed

Embed an anime episode with sub/dub selection, resolved through MVP (default), KaF, Neko, or RU MV.

ParameterRequiredDescription
{id}YesTMDB TV id, IMDB id (tt...), MAL id (mal-...), or AniList id (anilist-... / al-...) - see the callout below. Anime is always the TMDB TV namespace, even for movies
{episode}YesEpisode number within season
seasonOptionalDefaults to 1
audioOptionalsub (default) or dub
serverOptionalmega (default), kaa, neko, or ru - see Providers
startAtOptionalSeconds into the video to start playback at - see Resume Playback
autoplayOptional1 to start playback automatically once the stream loads
autonextOptional1 to load episode + 1 (same season/audio/server) automatically when this one ends
HTML URL
<iframe src="https://your-domain.example/embed/anime/{id}/{episode}?season=1&audio=dub" width="100%" height="560" allowfullscreen></iframe>
# Bleach S1E1, dub, default (MVP)/embed/anime/30984/1?season=1&audio=dub# Sub, season 2, forced KaF/embed/anime/30984/1?season=2&server=kaa# Dub, Neko (falls back to MVP automatically if unavailable)/embed/anime/30984/1?audio=dub&server=neko# RU MV - only succeeds when we have a confident title match/embed/anime/30984/1?server=ru# binge mode - autoplay + auto-advance to episode 2, 3... on end/embed/anime/30984/1?audio=dub&autoplay=1&autonext=1# same show, by MAL id instead of TMDB id/embed/anime/mal-269/1?audio=dub# same show, by AniList id (anilist- and al- both work)/embed/anime/anilist-269/1?audio=dub
Most other anime embed APIs are built around MAL or AniList ids - we're the outlier keying everything off TMDB, so a plain numeric {id} always means TMDB (unchanged, existing behavior). To pass a MAL or AniList id instead, prefix it: mal-269 or anilist-269 (al-269 also works, same thing). TMDB/MAL/AniList ids are all bare integers with overlapping ranges, so the prefix is what disambiguates them - an unprefixed number is never treated as a MAL/AniList id, even if the numbers happen to collide. This only applies to the anime endpoint; Movie/TV Show Embed stay TMDB/IMDB-only since MAL and AniList don't catalog live-action titles. First use of a given MAL/AniList id costs one extra lookup (cached in our own DB after that) to translate it to the TMDB id we actually resolve playback from.
Captions are borrowed from KaF's own (usually multi-language) subtitle set regardless of which server you pick, since MVP/Neko/RU MV don't carry a useful subtitle track of their own. Kino (movie/TV) instead pulls real OpenSubtitles-backed captions directly.

Query Parameters

Parameters supported today, plus what's still planned.

ParameterTypeStatusDescription
serverString LiveForce a specific provider - see Providers for values per endpoint
seasonInteger LiveAnime embed only - defaults to 1
audiosub / dub LiveAnime embed only - defaults to sub
startAtFloat LiveSeconds into the video to start playback at - see Resume Playback
autoplay0 / 1 LiveStarts playback automatically once the stream loads - muted-retry fallback if the browser blocks unmuted autoplay
autonext0 / 1 LiveTV/anime only - loads episode + 1 (same season, same server/audio) when the current one ends. Doesn't roll over to the next season
GET /api/public/episode-progress

Episode Release Progress

How many episodes of a season we've actually got cached (sub/dub/RU-dub counts), whether the season is finished, and an upcoming air date when we have one. Read-only, rate limited to 40 requests/minute per IP (separate from every other limiter on this page).

ParameterRequiredDescription
tmdbIdOne of tmdbId/titleTMDB TV id - the reliable lookup, checked first if both are given
titleOne of tmdbId/titleOnly used when tmdbId isn't given - matched against a normalized title, so casing/punctuation don't need to match exactly
seasonOptionalOnly applies with tmdbId - defaults to 1
Fetch URL
fetch('https://your-domain.example/api/public/episode-progress?tmdbId=30984&season=4')
  .then(function (r) { return r.json(); })
  .then(function (data) { console.log(data); });
# Bleach, season 4/api/public/episode-progress?tmdbId=30984&season=4# by title instead of tmdbId/api/public/episode-progress?title=Bleach
Response
{
  "ok": true,
  "found": true,
  "data": {
    "tmdbId": 30984,
    "season": 4,
    "title": "Bleach",
    "subEpisodes": 28,
    "dubEpisodes": 28,
    "totalEpisodes": 28,
    "ruDubEpisodes": 28,
    "isFinished": true,
    "tmdbSeasonEpisodeCount": 28,
    "upcomingDate": null,
    "source": "anikoto+native",
    "cachedAt": 1767225600000
  }
}

found: false (with an ok: true, 200) means we don't have this title/season cached yet - not an error, just nothing to report. A genuinely malformed request (neither tmdbId nor title given) returns 400.

GET /api/public/tmdb-mapping

Anime TMDB Mapping

Every other anime embed API out there is built around MAL or AniList ids - we're the outlier keying everything off TMDB (see the callout on Anime Embed). This translates between them: hand it a TMDB id we've already mapped and get back its MAL/AniList id, or the reverse via title. Same 40 requests/minute per IP limit as Episode Release Progress, read-only.

ParameterRequiredDescription
tmdbIdOne of tmdbId/titleTMDB TV id - the reliable lookup key, exact match (a tmdbId can span multiple AniList seasons; this returns the best match)
titleOne of tmdbId/titleOnly used when tmdbId isn't given - case-insensitive match against whatever title string got cached, not a fuzzy/normalized match
Fetch URL
fetch('https://your-domain.example/api/public/tmdb-mapping?tmdbId=30984')
  .then(function (r) { return r.json(); })
  .then(function (data) { console.log(data); });
# Bleach/api/public/tmdb-mapping?tmdbId=30984# by title instead of tmdbId/api/public/tmdb-mapping?title=Bleach
Response
{
  "ok": true,
  "found": true,
  "data": {
    "tmdbId": 30984,
    "malId": 269,
    "anilistId": 269,
    "title": "Bleach",
    "cachedAt": 1767225600000,
    "validatedAt": 1767225600000
  }
}

We only have a mapping cached for a title once something on the site has actually resolved it before - this isn't a full TMDB↔MAL/AniList directory, just our own cache. found: false means we don't have it yet.

Security

The raw upstream stream URL is never exposed anywhere - not in the page source, not in a JSON response, not at any layer of a nested playlist. Every embed resolves the real stream server-side and hands the player a session-bound, encrypted, expiring proxy URL instead - only /api/m3u8-proxy ever talks to the real CDN, and only for the exact browser session that resolved it. Copying a resolved stream URL out of your browser's network tab and loading it in a different browser/session returns 403 This link does not belong to your session - verified live.

Player Events

The embedded player posts postMessage events to the parent window at every play, pause, seek-ready, and end, plus a throttled position update (at most once every 5 seconds) while playing - so your page can track playback state without ever reaching into the iframe (which same-origin policy blocks anyway).

JS
window.addEventListener('message', function (e) {
  if (!e.data || e.data.source !== 'anikino-embed') return;
  // e.data.event: 'ready' | 'play' | 'pause' | 'timeupdate' | 'ended' | 'autonext'
  // e.data.currentTime / e.data.duration: seconds (floats)
  // 'autonext' also carries e.data.nextEpisodeUrl - fired right before the iframe
  // navigates itself there (only when ?autonext=1 was set - see Query Parameters)
  console.log(e.data.event, e.data.currentTime, e.data.duration);
});
Sent with target origin * since the embed genuinely doesn't know what domain is hosting it - same tradeoff YouTube/Vimeo embeds make. The payload only ever carries playback position, nothing sensitive.

Resume Playback

Save and restore a viewer's watch position using the timeupdate event above and the startAt query parameter - same idea as VidSrc's resume flow. Listen for timeupdate, store currentTime against that title/episode in your own backend or localStorage, then next time build the iframe src with ?startAt=<seconds> and the player seeks there as soon as its metadata loads - no extra round trip needed.

JS
var lastKnownPosition = 0;
window.addEventListener('message', function (e) {
  if (!e.data || e.data.source !== 'anikino-embed') return;
  if (e.data.event === 'timeupdate' || e.data.event === 'pause') {
    lastKnownPosition = e.data.currentTime;
    localStorage.setItem('resume:293660', lastKnownPosition);
  }
});

// next visit - resume where they left off
var resumeAt = localStorage.getItem('resume:293660') || 0;
iframe.src = '/embed/movie/293660?startAt=' + resumeAt;
Building the above yourself is optional - every embed already does this automatically, entirely in the viewer's own browser (nothing sent to us). It saves progress to its own localStorage as the viewer watches and resumes from there on the next load of that same title/episode, scoped to our origin so it follows the viewer across every site that embeds us. An explicit ?startAt= from your own page (like the example above) always takes priority over it, so implementing your own resume flow never conflicts with this - it's just a fallback for when you don't.

Captions Settings

Every player - /embed/* and the main site's own player alike - has a Captions Settings entry under the gear icon's Settings menu, right below Captions. It's there because different providers ship separately-timed rips of the same episode, so a caption borrowed from a different provider than the one actually serving the video can drift out of sync by a few real seconds - not something fixable at the source, so the viewer gets a manual nudge instead.

ControlOptionsDefault
SizeS / M / L / XL / XXL / XXXLM
ColorWhite / Black / Yellow / RedWhite
BoldOff / OnOff
BackgroundOff / OnOn (a dark box behind the text keeps it readable over busy scenes)
Sync Offset-1s / +1s, cumulative+0s

Saved to the viewer's own localStorage, per device - not sent to us, and not something a host page can read or set through the embed API. Sync Offset works by directly shifting the loaded caption track's cue timing (not the video), so it applies instantly with no reload, and persists across episode/server switches until changed again.

Test Your Embed

Questions & Support

Got a question about the embed API, found a bug, or want a title added or fixed? Send it here.

Last Updated

This page and the embed API were last updated August 26, 2026.