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

Build docs developers (and LLMs) love