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 [] >>
The page number for pagination
Cache Duration: 1 hour (3600 seconds)
Usage Example
Response Example
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 []>>
The search query string (automatically URL-encoded)
The page number for pagination
Cache Duration: 1 hour (3600 seconds)
Usage Example
Server Component
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 >>
The MyAnimeList ID of the anime
Cache Duration: 24 hours (86400 seconds)
Usage Example
Dynamic Route
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 []>>
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 []>>
The MyAnimeList ID of the anime
The page number for pagination
Cache Duration: 24 hours (86400 seconds)
Usage Example
Episode List Component
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 ;
}
The unique MyAnimeList ID
Direct URL to the anime on MyAnimeList
Contains JPG and WebP image variants with multiple sizes
The main title of the anime
English translated title (may be null)
Original Japanese title (may be null)
Anime type: TV, Movie, OVA, Special, ONA, Music
Total number of episodes (null for ongoing/unknown)
MyAnimeList user score (0-10 scale)
Anime description/synopsis
Airing season: spring, summer, fall, winter
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 ;
}
Show Episode Field Descriptions
Indicates if the episode is filler content (not from source material)
Indicates if the episode is a recap episode
Episode duration in seconds
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:
Function Cache Duration Reason getTopAnime1 hour Top lists change frequently searchAnime1 hour Search results may update getAnimeById24 hours Individual anime data is stable getAnimeCharacters24 hours Character lists rarely change getAnimeEpisodes24 hours Episode 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
Show 1. Handle Loading States
Always show loading states when fetching data from the Jikan API: 'use client' ;
import { useState , useEffect } from 'react' ;
import { getTopAnime } from '@/services/jikan' ;
export default function TopAnimeList () {
const [ anime , setAnime ] = useState ([]);
const [ loading , setLoading ] = useState ( true );
useEffect (() => {
getTopAnime (). then ( response => {
setAnime ( response . data );
setLoading ( false );
});
}, []);
if ( loading ) return < div > Loading ...</ div > ;
return < div >{ /* render anime */ } </ div > ;
}
Always wrap API calls in try-catch blocks: try {
const response = await getAnimeById ( id );
return response . data ;
} catch ( error ) {
if ( error . message . includes ( 'rate limit' )) {
// Show rate limit message
} else {
// Show generic error
}
}
Always use the provided TypeScript interfaces: import type { JikanAnime , JikanResponse } from '@/services/jikan' ;
function processAnime ( anime : JikanAnime ) {
// TypeScript will ensure type safety
console . log ( anime . mal_id );
}
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