Skip to main content

Overview

The searchAnime() function searches MyAnimeList’s anime database using a query string. Returns paginated results with metadata.

Function Signature

searchAnime(
  query: string,
  page?: number,
  limit?: number
): Promise<JikanSearchResponse>

Parameters

query
string
required
Search query string. Can include:
  • Anime titles (English or Japanese)
  • Partial matches (e.g., “attack” matches “Attack on Titan”)
  • Special characters (automatically URL-encoded)
Example: "Naruto", "進撃の巨人", "demon slayer"
page
number
default:"1"
Page number for pagination. Must be a positive integer.
  • Minimum: 1
  • Default: 1
  • Use pagination.last_visible_page to determine max page
limit
number
default:"20"
Number of results per page.
  • Minimum: 1
  • Maximum: 25 (API limitation)
  • Default: 20

Return Value

Returns a Promise that resolves to a JikanSearchResponse object.
data
JikanAnime[]
required
Array of anime objects matching the search query. Each object contains full anime details.See JikanAnime interface for complete structure.
pagination
object
required
Pagination metadata for navigating results

Examples

import { searchAnime } from '@/lib/animeApi';

const results = await searchAnime('Naruto');

console.log(`Found ${results.data.length} results`);
console.log(`Page ${results.pagination.current_page} of ${results.pagination.last_visible_page}`);

results.data.forEach(anime => {
  console.log(`- ${anime.title} (Score: ${anime.score})`);
});

Advanced Usage

import { searchAnime, type JikanAnime } from '@/lib/animeApi';

const allResults: JikanAnime[] = [];
let page = 1;
let hasMore = true;

while (hasMore && page <= 3) { // Limit to 3 pages
  const response = await searchAnime('action', page, 25);
  
  allResults.push(...response.data);
  hasMore = response.pagination.has_next_page;
  page++;
}

console.log(`Collected ${allResults.length} anime total`);

Filtering Results

const results = await searchAnime('anime', 1, 25);

// Filter for highly-rated anime only
const topRated = results.data.filter(anime => anime.score >= 8.0);

console.log(`Found ${topRated.length} anime with score >= 8.0`);
topRated.forEach(anime => {
  console.log(`${anime.title}: ${anime.score}/10`);
});

React Hook Usage

For React components, use the useAnimeSearch hook:
import { useAnimeSearch } from '@/hooks/useAnime';

function SearchResults({ query }: { query: string }) {
  const { data, loading, error, hasNextPage } = useAnimeSearch(query);
  
  if (loading) return <div>Searching...</div>;
  if (error) return <div>Error: {error}</div>;
  if (data.length === 0) return <div>No results found</div>;
  
  return (
    <div>
      <h2>Found {data.length} anime</h2>
      {data.map(anime => (
        <div key={anime.id}>
          <h3>{anime.title}</h3>
          <p>{anime.synopsis}</p>
        </div>
      ))}
      {hasNextPage && <button>Load More</button>}
    </div>
  );
}

Performance Considerations

Caching

Searches are automatically cached for 5 minutes:
// First call - fetches from API
const results1 = await searchAnime('Naruto');

// Within 5 minutes - returns cached data
const results2 = await searchAnime('Naruto');

// After 5 minutes - fetches fresh data
const results3 = await searchAnime('Naruto');

Rate Limiting

The function handles rate limiting automatically:
  • Maximum 3 requests per second
  • Requests are queued when limit reached
  • Automatic retry with exponential backoff on 429 errors
// All three searches will be queued automatically
const [anime1, anime2, anime3] = await Promise.all([
  searchAnime('Naruto'),
  searchAnime('One Piece'),
  searchAnime('Bleach')
]);

// Executes in sequence: 0ms, 350ms, 700ms

Optimization Tips

Debounce search inputs: Wait for user to stop typing before searching
import { debounce } from 'lodash';

const debouncedSearch = debounce(async (query: string) => {
  const results = await searchAnime(query);
  setResults(results);
}, 300);
Use appropriate page limits: Smaller limits (10-15) load faster and reduce bandwidth
// Good for mobile
await searchAnime('anime', 1, 10);

// Good for desktop
await searchAnime('anime', 1, 25);

Common Use Cases

async function autocomplete(partial: string) {
  if (partial.length < 3) return [];
  
  const results = await searchAnime(partial, 1, 5);
  return results.data.map(anime => ({
    label: anime.title,
    value: anime.mal_id,
    image: anime.images.jpg.small_image_url
  }));
}

Search with Filters

interface SearchFilters {
  query: string;
  minScore?: number;
  type?: string;
  status?: string;
}

async function filteredSearch(filters: SearchFilters) {
  const results = await searchAnime(filters.query);
  
  return results.data.filter(anime => {
    if (filters.minScore && anime.score < filters.minScore) return false;
    if (filters.type && anime.type !== filters.type) return false;
    if (filters.status && anime.status !== filters.status) return false;
    return true;
  });
}

// Usage
const topTVShows = await filteredSearch({
  query: 'action',
  minScore: 8.0,
  type: 'TV',
  status: 'Finished Airing'
});

Error Handling

Possible errors:
API request failed
Error
Network error or non-200 status code
try {
  await searchAnime('test');
} catch (error) {
  // Check if network error
  if (error instanceof TypeError) {
    console.error('Network error');
  }
}
Max retries exceeded
Error
Failed after 3 retry attempts
try {
  await searchAnime('test');
} catch (error) {
  if (error.message === 'Max retries exceeded') {
    console.error('API is down or unreachable');
  }
}

API Endpoint

This function calls the Jikan API endpoint:
GET https://api.jikan.moe/v4/anime?q={query}&page={page}&limit={limit}
See Jikan API Documentation for endpoint details.

See Also

getTopAnime()

Get highest-rated anime without search query

getSeasonalAnime()

Browse anime by season and year

JikanAnime Interface

Complete type definition for anime objects

Database Queries

Store and retrieve anime entries from database

Build docs developers (and LLMs) love