Free Β· No Auth Β· REST Β· JSON Β· CORS enabled

AniPub API

Anime metadata, streaming links, MAL integration, characters & voice actors β€” all from one clean, free API.

…Anime
12Endpoints
FreeNo Key
CORSOpen
https://anipub.xyz
GET/api/info/:idinteger or slug like one-piece
GET/api/find/:namecheck existence + episode count
GET/api/findbyGenre/:genre?Page=2paginated genre results
GET/api/findbyrating?page=1top-rated, paginated
GET/api/search/:namequick search Β· flat array
GET/api/searchall/:name?page=1paginated full search
POST/api/checkname + genre match check
GET/api/getlastlast document ID
GET/api/sort?name&genre&ratefrom&rateto&pageadvanced filter

Introduction

All responses are JSON. CORS is enabled so you can call directly from the browser. Standard HTTP status codes. No API key needed.

πŸ’‘
Image URLs: When ImagePath or Cover don't begin with https://, prepend https://anipub.xyz/ to build the full URL.
πŸ“Œ
ID Formats: /api/info/:id accepts an integer or a slug. Slugs use lowercase, spaces become -, no special characters. Examples: one-piece, black-clover, high-school-dxd, date-a-live-iv.

Error Codes

StatusNameDescription
200OKRequest succeeded β€” JSON returned
400Bad RequestMissing or invalid parameters
404Not FoundNo anime with that ID or name
500Server ErrorInternal error β€” retry
GET /api/info/:id

Get Anime Info

Full metadata for one anime. :id is an integer or a kebab-case slug (spaces β†’ hyphens, no special chars, lowercase).

βœ…
Works with: /api/info/61  Β·  /api/info/black-clover  Β·  /api/info/one-piece  Β·  /api/info/high-school-dxd
Path Parameter
id integer or string required Numeric ID like 61 or slug like black-clover
javascript
async function getAnimeInfo(idOrSlug) {
  // idOrSlug can be:  61  OR  "black-clover"  OR  "one-piece"
  const res  = await fetch(`https://anipub.xyz/api/info/${idOrSlug}`);
  if (!res.ok) throw new Error(res.status);
  const d = await res.json();

  // Resolve relative image paths
  const fix = p => p?.startsWith('https://') ? p : `https://anipub.xyz/${p}`;
  d.ImagePath = fix(d.ImagePath);
  d.Cover     = fix(d.Cover);
  return d;
}

// By ID
await getAnimeInfo(61);

// By slug  (spaces β†’ hyphens, lowercase, no special chars)
await getAnimeInfo('black-clover');
await getAnimeInfo('one-piece');
await getAnimeInfo('high-school-dxd');
python
import requests

def get_anime_info(id_or_slug):
    # Works with int ID or slug string
    r = requests.get(f"https://anipub.xyz/api/info/{id_or_slug}")
    r.raise_for_status()
    d = r.json()
    for k in ("ImagePath", "Cover"):
        if d.get(k) and not d[k].startswith("https://"):
            d[k] = f"https://anipub.xyz/{d[k]}"
    return d

print(get_anime_info(61)["Name"])           # integer ID
print(get_anime_info("one-piece")["Name"])   # slug
php
function getAnimeInfo($idOrSlug) {
    $url  = "https://anipub.xyz/api/info/" . $idOrSlug;
    $data = json_decode(file_get_contents($url), true);
    foreach (['ImagePath', 'Cover'] as $k)
        if (!str_starts_with($data[$k] ?? '', 'https://'))
            $data[$k] = 'https://anipub.xyz/' . $data[$k];
    return $data;
}
print_r(getAnimeInfo(61));             // integer
print_r(getAnimeInfo('one-piece'));   // slug
bash
# Integer ID
curl "https://anipub.xyz/api/info/61"

# Slug β€” lowercase, hyphens for spaces, no special chars
curl "https://anipub.xyz/api/info/black-clover"
curl "https://anipub.xyz/api/info/one-piece"
curl "https://anipub.xyz/api/info/high-school-dxd"

curl "https://anipub.xyz/api/info/one-piece" | jq '.'
go
type AnimeInfo struct {
    ID        int      `json:"_id"`
    Name      string   `json:"Name"`
    ImagePath string   `json:"ImagePath"`
    MALScore  string   `json:"MALScore"`
    Genres    []string `json:"Genres"`
    Status    string   `json:"Status"`
    EpCount   int      `json:"epCount"`
}

// idOrSlug: "61" or "black-clover"
resp, _ := http.Get("https://anipub.xyz/api/info/black-clover")
defer resp.Body.Close()
var a AnimeInfo
json.NewDecoder(resp.Body).Decode(&a)
if !strings.HasPrefix(a.ImagePath, "https://") {
    a.ImagePath = "https://anipub.xyz/" + a.ImagePath
}
ruby
require 'net/http'; require 'json'

def get_info(id_or_slug)
  d = JSON.parse(Net::HTTP.get(URI(
    "https://anipub.xyz/api/info/#{id_or_slug}"
  )))
  ['ImagePath', 'Cover'].each { |k|
    d[k] = "https://anipub.xyz/#{d[k]}" unless d[k].to_s.start_with?('https://') }
  d
end

p get_info(61)['Name']
p get_info('one-piece')['Name']
GET https://anipub.xyz/api/info/black-clover
200 OKapplication/json
{
  "_id": 61,
  "Name": "Black Clover",
  "ImagePath": "https://...",   // prepend https://anipub.xyz/ if relative
  "Cover": "https://...",
  "Synonyms": "...",
  "Aired": "Oct 3, 2017 to Mar 30, 2021",
  "Premiered": "Fall 2017",
  "RatingsNum": 45,
  "Genres": ["action", "fantasy", "magic"],
  "Studios": "Pierrot",
  "DescripTion": "...",
  "Duration": "25m",
  "MALScore": "8.88",
  "Status": "Finished Airing",
  "epCount": 170
}
GET/api/getAll

Get Total Count

Returns a plain integer β€” total anime in the database. Use to determine valid ID range for /api/info/:id.

javascript
const total = await (await fetch('https://anipub.xyz/api/getAll')).json();
console.log(`${total} anime β€” IDs 1 to ${total}`);
python
total = requests.get("https://anipub.xyz/api/getAll").json()
print(f"Total: {total}")
bash
curl "https://anipub.xyz/api/getAll"
# β†’ 153
php
$n = json_decode(file_get_contents('https://anipub.xyz/api/getAll'));
echo "Total: $n";
GET https://anipub.xyz/api/getAll
GET/api/find/:name

Find Anime by Name

Check if an anime exists. Returns {"exist":true,"id":10,"ep":1155} or {"exist":false}.

Path Parameter
namestringrequiredURL-encoded name β€” spaces as %20
javascript
const findAnime = async name => (await fetch(
  `https://anipub.xyz/api/find/${encodeURIComponent(name)}`
)).json();

const r = await findAnime('One Piece');
// β†’ { exist: true, id: 10, ep: 1155 }

if (r.exist) {
  const info = await fetch(`https://anipub.xyz/api/info/${r.id}`).then(x => x.json());
}
python
from urllib.parse import quote
r = requests.get(f"https://anipub.xyz/api/find/{quote('One Piece')}").json()
if r["exist"]:
    print(f"ID={r['id']}  ep={r['ep']}")
bash
curl "https://anipub.xyz/api/find/One%20Piece"
# β†’ {"exist":true,"id":10,"ep":1155}

curl "https://anipub.xyz/api/find/Does%20Not%20Exist"
# β†’ {"exist":false}
go
type FindResult struct { Exist bool `json:"exist"`; ID int `json:"id"`; Ep int `json:"ep"` }
resp, _ := http.Get("https://anipub.xyz/api/find/" + url.PathEscape("One Piece"))
var r FindResult
json.NewDecoder(resp.Body).Decode(&r)
GET https://anipub.xyz/api/find/One%20Piece
GET/v1/api/details/:id

Streaming Links

Returns iframe streaming links per episode. Strip the src= prefix from each link value to get the raw iframe URL.

πŸ“Œ
Episode mapping: local.link (top-level) = Episode 1. The local.ep[] array starts from Episode 2 onwards β€” so ep array index 0 = EP 2, index 1 = EP 3, etc.
⚠️
_id may be a MongoDB ObjectId string β€” deprecated. Use the URL :id parameter as the canonical identifier.
javascript
const { local } = await (await fetch(
  'https://anipub.xyz/v1/api/details/119'
)).json();

const src = link => link.replace('src=', '');

// EP 1 β€” top-level link (NOT in the ep array)
iframeEl.src = src(local.link);

// EP 2, 3, 4... β€” ep[] array starts at episode 2
const allEps = [
  { ep: 1, src: src(local.link) },               // EP 1
  ...local.ep.map((e, i) => ({ ep: i+2, src: src(e.link) })) // EP 2+
];
python
local = requests.get("https://anipub.xyz/v1/api/details/119").json()["local"]

# EP 1 is the top-level link, NOT inside ep[]
ep1 = local["link"].replace("src=", "")
print(f"EP1: {ep1}")

# EP 2, 3, 4... β€” ep[] array begins at episode 2
for i, ep in enumerate(local["ep"], 2):
    print(f"EP{i}: {ep['link'].replace('src=','')}")
bash
curl "https://anipub.xyz/v1/api/details/119" | \
  jq '.local.ep[].link | ltrimstr("src=")'
php
$local = json_decode(file_get_contents(
  'https://anipub.xyz/v1/api/details/119'
), true)['local'];
foreach ($local['ep'] as $i => $ep)
    echo "EP".($i+1).": ".str_replace('src=','',$ep['link'])."\n";
GET https://anipub.xyz/v1/api/details/119
200 OKapplication/json
{
  "local": {
    "name": "Black Clover",
    "link": "src=https://...",  // ← strip "src=" β†’ EP 1 iframe src
    "ep": [
      { "link": "src=https://..." },  // ep[0] = EP 2
      { "link": "src=https://..." },  // ep[1] = EP 3
      "..."
    ]
  }
}
GET/anime/api/details/:id

Full Details + MAL + Characters

Returns local (metadata), jikan (MAL data), and characters (cast + voice actors). The most complete endpoint.

javascript
const { local, jikan, characters } = await (await fetch(
  'https://anipub.xyz/anime/api/details/119'
)).json();

const fix = p => p?.startsWith('https://') ? p : `https://anipub.xyz/${p}`;

console.log(local.Name, local.MALScore);
console.log(jikan?.synopsis);

characters.forEach(c => {
  console.log(c.character.name, c.role);
  c.voice_actors.forEach(va => console.log('  VA:', va.person.name, va.language));
});
python
data = requests.get("https://anipub.xyz/anime/api/details/119").json()
local, chars = data["local"], data["characters"]
print(local["Name"], local["MALScore"])
for c in chars:
    print(f"  {c['character']['name']} β€” {c['role']}")
bash
curl "https://anipub.xyz/anime/api/details/119" | jq '.'
curl "https://anipub.xyz/anime/api/details/119" | \
  jq '.characters[] | {name:.character.name, role}'
go
var res struct {
    Local struct{ Name string `json:"Name"` } `json:"local"`
    Characters []struct{
        Character struct{ Name string `json:"name"` } `json:"character"`
        Role string `json:"role"`
    } `json:"characters"`
}
json.NewDecoder(resp.Body).Decode(&res)
GET https://anipub.xyz/anime/api/details/119
GET/api/findbyGenre/:genre

Browse by Genre

Paginated anime list by genre. Add ?Page=2 for next pages. Response: {"currentPage":1,"wholePage":[...]}.

Parameters
genrestringpathe.g. harem, action, romance
Pageintegerquery Β· optionalPage number, default 1. Append ?Page=2.
javascript
const findByGenre = async (genre, page = 1) => {
  const data = await (await fetch(
    `https://anipub.xyz/api/findbyGenre/${genre}?Page=${page}`
  )).json();
  console.log(`Page ${data.currentPage} β€” ${data.wholePage.length} results`);
  return data.wholePage;
};

findByGenre('harem');       // page 1
findByGenre('harem', 2);    // page 2
findByGenre('action', 3);  // page 3
python
def find_by_genre(genre, page=1):
    d = requests.get(
        f"https://anipub.xyz/api/findbyGenre/{genre}?Page={page}"
    ).json()
    return d["wholePage"]

for a in find_by_genre("harem"):
    print(f"[{a['_id']}] {a['Name']} β€” {a['MALScore']}")
bash
curl "https://anipub.xyz/api/findbyGenre/harem"
curl "https://anipub.xyz/api/findbyGenre/harem?Page=2"
curl "https://anipub.xyz/api/findbyGenre/action" | jq '.wholePage[].Name'
php
$data = json_decode(file_get_contents(
    "https://anipub.xyz/api/findbyGenre/harem?Page=1"
), true);
foreach ($data['wholePage'] as $a)
    echo $a['Name'] . " β€” " . $a['MALScore'] . "\n";
GET https://anipub.xyz/api/findbyGenre/harem?Page=1
POST/api/check

Check Name & Genre

Verifies an anime exists by name and genre. Genre accepts a string or an array of strings.

Request Body (JSON)
NamestringrequiredAnime name to match
Genrestring | arrayrequired"Action" or ["Action","Drama"]
javascript
const checkAnime = async (name, genre) => (await fetch('https://anipub.xyz/api/check', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ Name: name, Genre: genre })
})).json();

checkAnime('Black Clover', 'Action');
checkAnime('Jujutsu Kaisen', ['Action', 'Drama']);
python
res = requests.post("https://anipub.xyz/api/check", json={
    "Name": "Jujutsu Kaisen", "Genre": ["Action", "Drama"]
}).json()
bash
curl -X POST "https://anipub.xyz/api/check" \
  -H "Content-Type: application/json" \
  -d '{"Name":"Black Clover","Genre":["Action","Fantasy"]}'
php
$ch = curl_init('https://anipub.xyz/api/check');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode(['Name'=>'Black Clover','Genre'=>['Action']]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
]);
axios
const { data } = await axios.post('https://anipub.xyz/api/check', {
  Name: 'Jujutsu Kaisen', Genre: ['Action', 'Drama']
});
GET/api/findbyrating

Browse by Rating

Returns a paginated list of top-rated anime, sorted by MAL score descending. Use ?page=2 to navigate pages.

Query Parameters
pageintegeroptionalPage number, default 1. Append ?page=2, ?page=3…
javascript
const getTopRated = async (page = 1) => {
  const data = await (await fetch(
    `https://anipub.xyz/api/findbyrating?page=${page}`
  )).json();
  // data.AniData β€” array of anime
  // data.currentPage β€” current page number
  return data;
};

const p1 = await getTopRated();      // page 1
const p2 = await getTopRated(2);   // page 2
p1.AniData.forEach(a => console.log(a.Name, a.MALScore));
python
def get_top_rated(page=1):
    d = requests.get(f"https://anipub.xyz/api/findbyrating?page={page}").json()
    return d.get("AniData", [])

for a in get_top_rated():
    print(f"{a['Name']} β€” {a['MALScore']}")
bash
curl "https://anipub.xyz/api/findbyrating"
curl "https://anipub.xyz/api/findbyrating?page=2"
curl "https://anipub.xyz/api/findbyrating?page=1" | jq '.AniData[].Name'
GET https://anipub.xyz/api/findbyrating?page=1
200 OKapplication/json
{
  "currentPage": 1,
  "AniData": [
    {
      "_id": 2454,
      "Name": "Frieren: Beyond Journey's End",
      "ImagePath": "https://...",
      "MALScore": "9.36",
      "RatingsNum": 50,
      "DescripTion": "...",
      "finder": "frieren-beyond-journeys-end"
    }
  ]
}
GET/api/searchall/:name

Search All (Paginated)

Full paginated search across all anime. Returns AniData array and currentPage. Use ?page=2 to navigate. More results than /api/search.

Parameters
namestringpathSearch query β€” URL-encode spaces as %20
pageintegerquery Β· optionalPage number, default 1. Append ?page=2.
javascript
const searchAll = async (query, page = 1) => (await fetch(
  `https://anipub.xyz/api/searchall/${encodeURIComponent(query)}?page=${page}`
)).json();

const r = await searchAll('One Piece');
// r.AniData β€” array of full anime objects
// r.currentPage β€” current page

r.AniData.forEach(a =>
  console.log(`[${a._id}] ${a.Name} β€” ${a.MALScore}`)
);
python
def search_all(query, page=1):
    return requests.get(
        f"https://anipub.xyz/api/searchall/{quote(query)}?page={page}"
    ).json()

r = search_all("One Piece")
for a in r.get("AniData", []):
    print(f"[{a['_id']}] {a['Name']} β€” {a['MALScore']}")
bash
curl "https://anipub.xyz/api/searchall/One%20Piece"
curl "https://anipub.xyz/api/searchall/One%20Piece?page=2"
curl "https://anipub.xyz/api/searchall/naruto" | jq '.AniData[].Name'
php
$d = json_decode(file_get_contents(
    "https://anipub.xyz/api/searchall/" . rawurlencode("One Piece") . "?page=1"
), true);
foreach ($d['AniData'] as $a)
    echo $a['Name'] . " β€” " . $a['MALScore'] . "\n";
GET https://anipub.xyz/api/searchall/One%20Piece?page=1
200 OKapplication/json
{
  "currentPage": 1,
  "AniData": [
    {
      "_id": 10,
      "Name": "One Piece",
      "ImagePath": "One-Piece.jpg",
      "MALScore": "8.72",
      "RatingsNum": 50,
      "DescripTion": "...",
      "finder": "one-piece"
    }
  ]
}
GET/api/getlast

Get Last Document ID

Returns the ID of the most recently added anime in the database. Useful for detecting new additions or setting an upper bound when iterating over IDs.

πŸ’‘
Poll this endpoint periodically to check if new anime have been added β€” compare against your cached last ID.
javascript
const lastId = await (await fetch(
  'https://anipub.xyz/api/getlast'
)).json();

// Returns a plain number β€” the latest anime _id
console.log(`Latest ID: ${lastId}`);

// Check for new anime since last visit
const cached = localStorage.getItem('lastId');
if (cached && lastId > +cached) {
  console.log(`${lastId - +cached} new anime added!`);
}
localStorage.setItem('lastId', lastId);
python
last_id = requests.get("https://anipub.xyz/api/getlast").json()
print(f"Latest anime ID: {last_id}")
bash
curl "https://anipub.xyz/api/getlast"
GET https://anipub.xyz/api/getlast
200 OKapplication/json
2946   // plain integer β€” the _id of the newest document
GET/api/sort

Advanced Sort & Filter

Flexible search with simultaneous name query, genre filtering, MAL score range, and pagination. All parameters are optional β€” combine any subset freely.

ℹ️
All query params are optional. Omit any you don't need. Multiple genres are comma-separated: genre=action,comedy
Query Parameters
namestringoptional Anime name to search for. URL-encode spaces as %20
genrestringoptional Comma-separated genres: action,comedy Β· romance,drama
ratefromnumberoptional Minimum MAL score (0–10). Default 0
ratetonumberoptional Maximum MAL score (0–10). Default 10
pageintegeroptional Page number, default 1. Navigate with ?page=2
javascript
const sortAnime = async (opts = {}) => {
  const p = new URLSearchParams();
  if (opts.name)     p.set('name',     opts.name);
  if (opts.genre)    p.set('genre',    opts.genre);
  if (opts.ratefrom) p.set('ratefrom', opts.ratefrom);
  if (opts.rateto)   p.set('rateto',   opts.rateto);
  if (opts.page)     p.set('page',     opts.page);
  return (await fetch(`https://anipub.xyz/api/sort?${p}`)).json();
};

// Examples:
await sortAnime({ name: 'One Piece', genre: 'action,comedy' });
await sortAnime({ genre: 'romance', ratefrom: '7', rateto: '10' });
await sortAnime({ ratefrom: '8', page: '2' }); // top scores pg 2
python
def sort_anime(name=None, genre=None, ratefrom=0, rateto=10, page=1):
    params = {k: v for k, v in {
        'name': name, 'genre': genre,
        'ratefrom': ratefrom, 'rateto': rateto, 'page': page
    }.items() if v is not None}
    return requests.get("https://anipub.xyz/api/sort", params=params).json()

# Examples:
sort_anime(name="One Piece", genre="action,comedy")
sort_anime(genre="romance", ratefrom=7, rateto=10)
sort_anime(ratefrom=8, page=2)
bash
# Name + genre filter
curl "https://anipub.xyz/api/sort?name=One%20Piece&genre=action,comedy"

# Score range filter
curl "https://anipub.xyz/api/sort?ratefrom=8&rateto=10&page=1"

# Genre only, paginated
curl "https://anipub.xyz/api/sort?genre=romance,drama&page=2"
php
$data = json_decode(file_get_contents(
    "https://anipub.xyz/api/sort?" . http_build_query([
        'name'     => 'One Piece',
        'genre'    => 'action,comedy',
        'ratefrom' => 0,
        'rateto'   => 10,
        'page'     => 1,
    ])
), true);
foreach ($data[1] as $a)
    echo $a['Name'] . " β€” " . $a['MALScore'] . "
";
GET https://anipub.xyz/api/sort?ratefrom=0&rateto=10&page=1
200 OKapplication/json
[
  2,          // [0] total page count
  [           // [1] array of anime results
    {
      "_id":       1735,
      "Name":      "One Piece: The Log of the Rivalry!...",
      "ImagePath": "https://cdn.noitatnemucod.net/...",
      "MALScore":  "6.46",
      "Genres":    ["Action", "Adventure", "Comedy"],
      "finder":    "one-piece-the-log-of-the-rivalry-..."
    }
  ]
]
ℹ️
Response is a 2-element array: [0] = total page count, [1] = the anime results array. Access results via data[1].

⚑ Playground

Fire any request directly. Supports GET and POST.

🎌 Genre Browser

Click any tag to instantly fetch and display live API results.

Select a genre above to load results…