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
10Endpoints
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

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"
    }
  ]
}

⚡ 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…