Are You an Otaku? - Fetching Anime details with Jikan API

HiyomiHiyomi
5 min read

Project Structure

Jikan API

Jikan API is a free open-source anime API platform providing endpoints for various anime data-related functionalities. It is free, no API key is required, and gives real-time data, along with well-maintained documentation.

Project Features

Today we are going be building an API wrapper for Jikan API (providing named functions for tasks to fetch from endpoints directly).
It will comprise of three main parts -
1) Top 10 Animes based on selected Genres
2) Top 10 Animes based on the score on MyAnimeList Website
3) Top 10 Characters

A) Making requests to Jikan API

1) Fetching All Genres

Let's take a look at an example to fetch all genres of an anime.
Note: All the information will be based on the data available on the MyAnimeList site and Jikan API.

// RESPONSE SCHEMA FOR GENRE ENDPOINT   
{
  "data": [
    {
      "mal_id": 0, // unique ID for a genre
      "name": "string", // name of the genre
      "url": "string", // url for MyAnimeList of that genre
      "count": 0 // Number of animes based on that genre on MyAnimeList
    }
  ]
}

We request the API with the endpoint: https://api.jikan.moe/v4/genres/anime and convert it to a JSON object. Then we map the genre name to its unique ID and return this map. We will need those ID details later on for animes based on genre functionality.

import requests

BASE_URL = 'https://api.jikan.moe/v4/'

def get_all_genres():
    # fetch genres 
    genres = requests.get(BASE_URL + 'genres/anime').json()
    genre_map = {}
    # map genre name with their unique ID
    for genre in genres['data']:
        genre_map[genre['name']] = genre['mal_id']
    return genre_map

all_genres = get_all_genres()
print(all_genres)

2) Animes based on Genres

// RESPONSE SCHEMA FOR ANIME ENDPOINT
{
  "data": {
    "url": "string", // url of anime
    "images": { // cover images
      "jpg": {
        "image_url": "string",
        "small_image_url": "string",
        "large_image_url": "string"
      },
      "webp": {
        "image_url": "string",
        "small_image_url": "string",
        "large_image_url": "string"
      }
    },
    "trailer": { // youtube trailer of that anime
      "youtube_id": "string",
      "url": "string",
      "embed_url": "string"
    },
    "titles": [ // titles of the anime
      {
        "type": "string",
        "title": "string"
      }
    ]
    "status": "Finished Airing", // status - Finished Airing / Ongoing
    "score": 0, // score out of 10
    "rank": 0, // rank
    "synopsis": "string", // Summary of the Anime
    "season": "summer", // in which season anime was released
    "year": 0, // in which year that anime got released
    "genres": [ // genres of that anime
      {
        "mal_id": 0,
        "type": "string",
        "name": "string",
        "url": "string"
      }
    ]
  }
}

We request the API to fetch all animes based on genres along with some filters such as order_by and sort.

def get_anime_by_genre(genres=[], order='score', sort='desc'):
    # If no genre selected, then return empty list
    if not genres:
        return []
    genres_map = get_all_genres()
    # formatting genres id of form: 1,2,5
    genres = ','.join([str(genres_map[genre]) for genre in genres])
    # filter query to pass 
    query = f'?genres={genres}&order_by={order}&sort={sort}'
    animes = requests.get(BASE_URL + 'anime' + query).json()
    return animes['data'][:10]

3) Top 10 Animes

Fetching the top animes and returning the first 10 animes.

def get_top_animes():
    top_animes = requests.get(BASE_URL + 'top/anime').json()
    return top_animes['data'][:10]

4) Top 10 Characters

Fetching the top characters and returning the first 10 characters.

// RESPONSE SCHEMA FOR CHARACTER ENDPOINT
{
  "data": [
    {
      "url": "string", // url for character on MyAnimeList
      "images": { // images of that character
        "jpg": { 
          "image_url": "string",
          "small_image_url": "string"
        },
        "webp": {
          "image_url": "string",
          "small_image_url": "string"
        }
      },
      "name": "string", // name of that character
      "name_kanji": "string", // name in Japanese
      "nicknames": [ // other name of the character
        "string"
      ],
      "favorites": 0, // number of favorites 
      "about": "string" // description of the character
    }
  ]
}
def get_top_characters():
    top_characters = requests.get(BASE_URL + 'top/characters').json()
    return top_characters['data'][:10]

B) Creating a class for these functions

Now Let's provide a structure for anime-related functions in an organised skeleton of a JikanAPI class.

import requests

class JikanAPI:
    BASE_URL = "https://api.jikan.moe/v4/"

    def _request(self, endpoint):
        reqs = requests.get(self.BASE_URL + endpoint)
        return reqs.json()

    def get_top_animes(self):
        results = self._request("top/anime")
        return results['data'][:10]

    def get_top_characters(self):
        results = self._request("top/characters")
        return results['data'][:10]

    def get_genres(self):
        results = self._request("genres/anime")
        genre_map = {}
        for result in results['data']:
            genre_map[result['name']] = result['mal_id']
            # print(result['name'], result['url'], result['count'])
        return genre_map

    # filters: genres=1,2,3,...  , order_by["start_date" "end_date" "score" "rank"], sort = desc/asc
    def get_anime_by_genre(self, genres=[], order="score", sort="desc"):
        if not genres:
            return []
        genres_map = self.get_genres()
        genres = ','.join([str(genres_map[genre]) for genre in genres])
        query = f'?genres={genres}&order_by={order}&sort={sort}'
        results = self._request('anime'+query)
        return results['data'][:10] 

# jikanapi = JikanAPI() # Creating a JikanAPI object
# all_genres = jikanapi.get_genres() # accessing get_genres method
# print(all_genres)

We are done with fetching the data from the API. And look we have a beautiful wrapper with good namings to call those functions, without requesting the endpoints - which makes our callings organized, clean, error-free, and reusable.

C) What's next?

Now that we have a wrapper for the API, integrate this with your website/ any other application without intervening with the endpoints in your website code and use these functionalities for the given.

1) You can integrate the data from API and build a Django/ React Anime recommender site.
2) You can provide face-to-API by replicating the MyAnimeList Site [Of course see their guidelines & permissions on its usage]
3) Do some analysis on different animes - score, popularity, create a dashboard to display anime trends, etc.
4) Here we used only three/ four functionalities, you can explore the jikan API and add more features to your project.
5) Use different anime APIs like Jikan, Kitsu etc and compare their stats.

Now you have a powerful tool in hand, go out and apply for real-world goods.

Good Luck & Keep Exploring!

Au revoir

1
Subscribe to my newsletter

Read articles from Hiyomi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hiyomi
Hiyomi

I am a self-learned developer exploring new techs and trying to simplify concepts while doing fun projects, in my favorite language: python :)