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.
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.
<iframe
src="https://your-domain.example/embed/movie/{id}"
width="100%" height="560"
frameborder="0" allowfullscreen
></iframe>
{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. The line under it shows the hourly speed scan: how long resolving took, time to the first video chunk, download speed measured from our server, and whether the video comes straight from the provider's CDN to you (direct) or through our server (relay).
MVP anime, default
Broad anime coverage, usually the fastest to resolve.
server=megaKaF 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=kaaNeko anime
Lightweight, quick-resolving anime source - falls back to MVP automatically on titles it doesn't have.
server=nekoRU MV anime
Russian-dub anime source. Only serves a title when we're confident in the match - fails clearly rather than guessing wrong.
server=ruKino movie/TV, default
Our primary movie/TV source - broad English-language coverage.
server=kinoT1M movie/TV
English movie/TV source with its own large multi-language subtitle set - good alternate when Kino doesn't have a title.
server=t1mVixSrc movie/TV
English-preferred HLS movie/TV source with a plain-HTTP resolve - extra alternate on the player page (not available in /embed).
player onlyRU MV movie/TV
Russian-dub movie/TV source - single audio track, no subtitles of its own. Slow: its CDN throttles datacenter addresses, so it starts at the lowest quality.
server=ruRU NX movie/TV
Russian-dub movies and series with several dubs and subtitles. Plays straight from its CDN to your browser, so it costs our server no bandwidth. The site player's default for Russian titles.
VR movie/TV
Several named servers behind one button; the player picks one that actually serves segments. Resolving can take a while.
VR anime
The same VR source for anime titles.
AllAnime anime
An aggregator: each episode lists several video providers that differ a lot in speed and in whether we can play them. Sub plays progressively straight from its CDN (quality menu and subtitles included); a dub often only exists on a slow host.
AllAnime sometimes answers our server with a captcha challenge (a real browser from the same IP is let through, plain HTTP clients are refused in waves). When that happens the backend has the Cloudflare Turnstile solved by Capsolver (a few seconds, a cent or two) and resends the request with the token; its encryption key is re-captured automatically when it rotates, and answers are cached for 2 minutes so a burst of viewers costs one request. Fm-Hls (Byse player) streams are decrypted straight from the host's API, and providers that send protocol-relative links (Vg) are now read correctly. Vg itself is currently dead at the source: its host (listeamed.net) now redirects to a parked-domain ad page, so there is nothing to extract. Sw (StreamWish) is dead too: its mirrors answer "expired or deleted" for every file and streamwish.to now needs a real browser to load, so it shows as Blocked. Only Ak (sub) plays direct to you; Fm-Hls and Mp4 (dub) go through our server because their links are tied to the requesting IP or to a Referer.
Providers behind AllAnime (Solo Leveling episode 1, hourly) Checking...
Speed is measured from our server, which is what matters for providers that go through our relay; direct ones play from their CDN to your browser.
| Provider | Status | Route | Ping | First byte | Speed | Notes |
|---|---|---|---|---|---|---|
| Waiting for the first scan... | ||||||
Movie Embed
Embed a movie player. Resolves through Kino (default), T1M, or RU MV.
| Parameter | Required | Description |
|---|---|---|
{id} | Yes | TMDB movie id or IMDB id (tt...) |
server | Optional | kino (default), t1m, or ru - see Providers |
startAt | Optional | Seconds into the video to start playback at - see Resume Playback |
autoplay | Optional | 1 to start playback automatically once the stream loads |
<iframe src="https://your-domain.example/embed/movie/{id}" width="100%" height="560" allowfullscreen></iframe>
TV Show Embed
Embed a specific episode. Resolves through Kino (default), T1M, or RU MV.
| Parameter | Required | Description |
|---|---|---|
{id} | Yes | TMDB TV id or IMDB id (tt...) |
{season} | Yes | Season number |
{episode} | Yes | Episode number within that season |
server | Optional | kino (default), t1m, or ru - see Providers |
startAt | Optional | Seconds into the video to start playback at - see Resume Playback |
autoplay | Optional | 1 to start playback automatically once the stream loads |
autonext | Optional | 1 to load episode + 1 (same season) automatically when this one ends |
<iframe src="https://your-domain.example/embed/tv/{id}/{season}/{episode}" width="100%" height="560" allowfullscreen></iframe>
Anime Embed
Embed an anime episode with sub/dub selection, resolved through MVP (default), KaF, Neko, or RU MV.
| Parameter | Required | Description |
|---|---|---|
{id} | Yes | TMDB 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} | Yes | Episode number within season |
season | Optional | Defaults to 1 |
audio | Optional | sub (default) or dub |
server | Optional | mega (default), kaa, neko, or ru - see Providers |
startAt | Optional | Seconds into the video to start playback at - see Resume Playback |
autoplay | Optional | 1 to start playback automatically once the stream loads |
autonext | Optional | 1 to load episode + 1 (same season/audio/server) automatically when this one ends |
<iframe src="https://your-domain.example/embed/anime/{id}/{episode}?season=1&audio=dub" width="100%" height="560" allowfullscreen></iframe>
{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.
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.
| Parameter | Type | Status | Description |
|---|---|---|---|
server | String | Live | Force a specific provider - see Providers for values per endpoint |
season | Integer | Live | Anime embed only - defaults to 1 |
audio | sub / dub | Live | Anime embed only - defaults to sub |
startAt | Float | Live | Seconds into the video to start playback at - see Resume Playback |
autoplay | 0 / 1 | Live | Starts playback automatically once the stream loads - muted-retry fallback if the browser blocks unmuted autoplay |
autonext | 0 / 1 | Live | TV/anime only - loads episode + 1 (same season, same server/audio) when the current one ends. Doesn't roll over to the next season |
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).
| Parameter | Required | Description |
|---|---|---|
tmdbId | One of tmdbId/title | TMDB TV id - the reliable lookup, checked first if both are given |
title | One of tmdbId/title | Only used when tmdbId isn't given - matched against a normalized title, so casing/punctuation don't need to match exactly |
season | Optional | Only applies with tmdbId - defaults to 1 |
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); });
{
"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.
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.
| Parameter | Required | Description |
|---|---|---|
tmdbId | One of tmdbId/title | TMDB TV id - the reliable lookup key, exact match (a tmdbId can span multiple AniList seasons; this returns the best match) |
title | One of tmdbId/title | Only used when tmdbId isn't given - case-insensitive match against whatever title string got cached, not a fuzzy/normalized match |
fetch('https://your-domain.example/api/public/tmdb-mapping?tmdbId=30984')
.then(function (r) { return r.json(); })
.then(function (data) { console.log(data); });
{
"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).
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);
});
* 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.
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;
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.
| Control | Options | Default |
|---|---|---|
| Size | S / M / L / XL / XXL / XXXL | M |
| Color | White / Black / Yellow / Red | White |
| Bold | Off / On | Off |
| Background | Off / On | On (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.