Skip to main content

Overview

EpiNeko integrates with the Jikan API v4 to fetch anime data from MyAnimeList. The Jikan service provides a clean, typed interface with built-in caching and rate limit handling.
All Jikan API functions are located in src/services/jikan.ts:1 and include automatic caching and error handling.

Core Functions

getTopAnime

Fetches the top-rated anime from MyAnimeList with pagination support.
export const getTopAnime = async (page = 1): Promise<JikanResponse<JikanAnime[]>>
page
number
default:1
The page number for pagination
Cache Duration: 1 hour (3600 seconds)
import { getTopAnime } from '@/services/jikan';

// Fetch first page of top anime
const response = await getTopAnime(1);
console.log(response.data); // Array of JikanAnime objects
console.log(response.pagination); // Pagination metadata

searchAnime

Search for anime by query string with pagination support.
export const searchAnime = async (query: string, page = 1): Promise<JikanResponse<JikanAnime[]>>
query
string
required
The search query string (automatically URL-encoded)
page
number
default:1
The page number for pagination
Cache Duration: 1 hour (3600 seconds)
import { searchAnime } from '@/services/jikan';

// Search for anime
const results = await searchAnime('Naruto', 1);

// Search handles special characters
const results2 = await searchAnime('Re:Zero', 1);

getAnimeById

Fetch detailed information about a specific anime by its MyAnimeList ID.
export const getAnimeById = async (id: number): Promise<JikanResponse<JikanAnime>>
id
number
required
The MyAnimeList ID of the anime
Cache Duration: 24 hours (86400 seconds)
import { getAnimeById } from '@/services/jikan';

const response = await getAnimeById(5114);
const anime = response.data;

console.log(anime.title); // "Fullmetal Alchemist: Brotherhood"
console.log(anime.score); // 9.09
console.log(anime.episodes); // 64

getAnimeCharacters

Fetch the characters for a specific anime.
export const getAnimeCharacters = async (id: number): Promise<JikanResponse<any[]>>
id
number
required
The MyAnimeList ID of the anime
Cache Duration: 24 hours (86400 seconds)
import { getAnimeCharacters } from '@/services/jikan';

const response = await getAnimeCharacters(5114);
const characters = response.data;

getAnimeEpisodes

Fetch episode information for a specific anime with pagination.
export const getAnimeEpisodes = async (id: number, page = 1): Promise<JikanResponse<JikanEpisode[]>>
id
number
required
The MyAnimeList ID of the anime
page
number
default:1
The page number for pagination
Cache Duration: 24 hours (86400 seconds)
import { getAnimeEpisodes } from '@/services/jikan';

const response = await getAnimeEpisodes(5114, 1);
const episodes = response.data;

episodes.forEach((episode) => {
  console.log(`Episode ${episode.mal_id}: ${episode.title}`);
  console.log(`Filler: ${episode.filler}, Recap: ${episode.recap}`);
});

TypeScript Interfaces

JikanAnime

export interface JikanAnime {
  mal_id: number;
  url: string;
  images: {
    jpg: JikanImage;
    webp: JikanImage;
  };
  title: string;
  title_english: string | null;
  title_japanese: string | null;
  type: string;
  episodes: number | null;
  status: string;
  score: number | null;
  synopsis: string | null;
  background: string | null;
  season: string | null;
  year: number | null;
}

JikanImage

export interface JikanImage {
  image_url: string;
  small_image_url: string;
  large_image_url: string;
}

JikanEpisode

export interface JikanEpisode {
  mal_id: number;
  url: string;
  title: string;
  title_japanese: string | null;
  title_romanji: string | null;
  duration: number | null;
  aired: string | null;
  filler: boolean;
  recap: boolean;
  forum_url: string | null;
}

JikanResponse<T>

export interface JikanResponse<T> {
  data: T;
  pagination?: {
    last_visible_page: number;
    has_next_page: boolean;
    current_page: number;
    items: {
      count: number;
      total: number;
      per_page: number;
    };
  };
}

Rate Limiting & Error Handling

The Jikan API has rate limits. The service automatically handles rate limit errors:
// src/services/jikan.ts:48
if (response.status === 429) {
  throw new Error('Jikan API rate limit exceeded. Please try again later.');
}
When you receive a 429 error, the Jikan API is temporarily unavailable. The application should display a user-friendly message and retry the request after a delay.

Caching Strategy

All Jikan API calls use Next.js ISR (Incremental Static Regeneration) for caching:
FunctionCache DurationReason
getTopAnime1 hourTop lists change frequently
searchAnime1 hourSearch results may update
getAnimeById24 hoursIndividual anime data is stable
getAnimeCharacters24 hoursCharacter lists rarely change
getAnimeEpisodes24 hoursEpisode data is stable
// src/services/jikan.ts:43
async function jikanFetch<T>(endpoint: string, revalidate = 3600): Promise<T> {
  const response = await fetch(`${JIKAN_API_BASE}${endpoint}`, {
    next: { revalidate: revalidate }
  });
  // ...
}
You can adjust cache durations by modifying the revalidate parameter in each function call.

Best Practices

API Base URL

const JIKAN_API_BASE = 'https://api.jikan.moe/v4';
All requests are made to the Jikan API v4 endpoint. No API key is required.

Jikan API Documentation

Official Jikan API v4 documentation

MyAnimeList

Source data for all anime information