# MyAnimeList Scraper - Anime, Manga, Rankings & Scores (`abotapi/myanimelist-scraper`) Actor

Scrape MyAnimeList anime and manga by keyword, ranking chart or URL. Extract titles, scores, ranks, members, genres, studios, authors, synonyms and full synopses. Supports top, airing, upcoming, popular and favorite rankings, plus resume and incremental monitoring.

- **URL**: https://apify.com/abotapi/myanimelist-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 anime or manga records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## MyAnimeList Scraper: Anime & Manga Scores, Reviews, Characters & Charts

MyAnimeList Scraper turns MyAnimeList into a clean anime and manga data feed. Search by keyword, walk any of the five ranking charts, or paste detail-page links, and get titles, scores, member counts, synopses, genres, studios, reviews, characters, voice actors, staff and related entries for every anime or manga. Resume an interrupted run or schedule incremental runs that report only what changed, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Deep search.** Walks MyAnimeList search results forward through every page, not just page 1.
- **Five charts.** Covers all five ranking charts (top, airing, upcoming, most popular, most favourited) for anime and manga.
- **Rich records.** Enriches records with related entries, character and staff previews, voice actors, paginated reviews and full stats.
- **Paste links.** Reads pasted detail-page links, anime and manga mixed, auto-detected.
- **Built for schedules.** Resume from a previous run and incremental monitoring are built in.
- **One row per title.** Keeps the main dataset to one row per anime or manga, including nested review and preview lists.
- **Safe on stop.** Pushes each record the moment it is ready, so a stopped run keeps its data.

### Use Cases

- **Anime and manga database or wiki sites:** build a structured catalogue from search or ranking-chart walks, complete with synopsis, genres, related entries and staff.
- **Fan communities, bots and newsletters:** watch a favourite chart or a saved list of titles with incremental mode, and post only what is NEW, UPDATED or REAPPEARED since the last run.
- **Review and sentiment research:** collect review text, ratings and recommendation tags across many titles for analysis.
- **Discovery and recommendation tools:** follow related-entry links plus character and voice-actor previews to map franchises and adaptations.
- **Market and studio tracking:** monitor the airing, upcoming and most-popular charts to see which studios and producers are trending.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

One record per anime or manga. With `fetchDetails` on (the default), the record also contains the entry's three review previews by default, plus the characters and voice actors shown there, staff, and related entries. Set `maxReviews` above 3 to read paginated review pages, or 0 to follow the available review pages up to a ceiling of 5000 reviews per record (the maximum for `maxReviews`). `reviewsComplete` tells you whether the collection reached the end of the visible pages. Some titles report more reviews than their pages expose; `reviewCountGap` records that difference. The character and staff lists remain previews. Other detail fields include Japanese, French and English titles, manga volumes and chapters, synonyms, episodes, ranked and popularity positions, favorites, licensors, producers, studios, authors, demographics, serialization, premiere, broadcast, source, duration, rating and review sentiment counts (`reviewBreakdown`).

| Field | Example |
| --- | --- |
| `id` | MyAnimeList's numeric id. (always present) |
| `mediaType` | anime or manga. (always present) |
| `title` | Main title. (always present) |
| `url` | Detail-page URL. (always present) |
| `type` | TV, Movie, Manga, and so on. On chart rows without details the count is included, for example `TV (12 eps)`. (most rows) |
| `score` | Weighted community score. (when scored) |
| `members` | Member count; present on ranking-chart listing rows even without details, and on every enriched record. (chart rows, details) |
| `scoredBy` | Number of users behind the weighted score. (details) |
| `reviewCount` | Total review count shown on the entry. (details) |
| `reviews` | Review text, author, date, recommendation, rating, URL, reader reaction total (`reactionCount`) and reaction breakdown (`reactions`); three previews by default or more with `maxReviews`. (details) |
| `reviewsFetched` | Number of reviews actually returned. (details) |
| `reviewsComplete` | True only when the review pages reached their end. (details) |
| `reviewPagesFetched` | Dedicated review pages successfully read; zero with the default previews. (details) |
| `reviewCountGap` | Reported total minus reviews available in the pages; null for a capped or interrupted pull. (complete review pulls) |
| `characters` | Character previews with role and nested `voiceActors` where available. (details) |
| `voiceActors` | Flat list of voice actors from the character previews. (anime details) |
| `staff` | Staff previews with name and role. (anime details) |
| `relatedEntries` | Related anime and manga with relationship, title and URL. (details) |
| `openingThemes` / `endingThemes` | Opening and ending song names. (anime details) |
| `genres` / `themes` | Genre and theme lists. (details) |
| `synopsis` | Full description text. (details) |
| `changeType` | NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED. (incremental) |
| `firstSeenAt` | First time this actor saw the record. (incremental) |
| `lastSeenAt` | Most recent time a run saw the record present (on an EXPIRED row, the last time before it disappeared). (incremental) |
| `changedFields` | Field names that differ from the previous run; populated only when `changeType` is UPDATED. (incremental) |

#### URL shapes

In **Paste URLs** mode every link below is understood. Detail and reviews links return one full record; search, chart, genre, season and studio links return their listed records (capped by Maximum items, enriched when Fetch full details is on).

| Shape | Example | Returns |
|---|---|---|
| Anime detail, with slug | `myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood` | One record |
| Anime detail, bare id | `myanimelist.net/anime/5114` | One record |
| Manga detail, with slug or bare id | `myanimelist.net/manga/2/Berserk`, `myanimelist.net/manga/2` | One record |
| Reviews page (anime or manga, any `?p=`) | `myanimelist.net/anime/5114/x/reviews?p=2` | The title's record; reviews follow Maximum reviews |
| Keyword search (anime or manga) | `myanimelist.net/anime.php?q=Frieren&cat=anime` | Matching records, walked page by page; extra filters such as `type` and a `show` offset are kept |
| Top chart (anime or manga) | `myanimelist.net/topanime.php?type=airing&limit=50` | Ranked records from that offset; `type` and `limit` are kept |
| Genre listing | `myanimelist.net/anime/genre/1/Action` | Records of the genre, page by page |
| Season (current or year/season) | `myanimelist.net/anime/season/2024/fall` | Every record of the season |
| Studio / producer | `myanimelist.net/anime/producer/1/Studio_Pierrot` | Records of the studio |

The mobile host `m.myanimelist.net` is mapped to the main site. Any other link is skipped with a warning in the log, never silently.

**Default URL list.** If you leave **MyAnimeList URLs** untouched in Paste URLs mode, the run reads the example list above (one link per shape) instead of failing; Maximum items still caps the total. Replace the list with your own links to scrape only those.

#### Rival-compatible fields

Every record also carries `malId`, `rank`, `titleSynonyms`, `airedFrom` and `airedTo` (ISO dates), `season`, `year`, `trailerUrl` (detail pages) and `scrapedAt`. They are derived from data already fetched and add no request. In incremental mode every one of them except `rank` is left out of change detection `rank` is the chart position on chart rows and is never replaced by the detail page; it is mirrored from `ranked` (the overall score rank) only on search and detail rows that have no chart position. A chart position move counts as UPDATED.

#### Output views

The dataset has four views: **Results** (every record, union of listing and detail columns), **Detail** (full title pages: synopsis, stats, credits, related entries, characters, staff, themes), **Reviews** (one row per review; the review's own `url` and `rating` replace the title's in this view) and **Changes** (what changed since the previous run, for incremental monitoring). A run summary is also saved under the `OUTPUT` key.

### How to Use

1. Pick a **Collection mode**: keyword search, a ranking chart, or pasted URLs.
2. Choose anime or manga and enter your keywords, chart or links.
3. Set **Maximum records**, and turn **Fetch full details** off if listing fields are enough.
4. Run it and download the dataset, or read it through the API.

Search walks every result page forward; charts walk the ranking; URL mode reads the links you paste.

**Search anime by keyword:**

```json
{
  "mode": "search",
  "mediaType": "anime",
  "searchTerms": ["space opera"],
  "maxItems": 50,
  "fetchDetails": true
}
```

**Currently airing chart, listing fields only:**

```json
{
  "mode": "charts",
  "mediaType": "anime",
  "chartKind": "airing",
  "maxItems": 100,
  "fetchDetails": false
}
```

**Paste links of both kinds in one run:**

```json
{
  "mode": "url",
  "urls": [
    "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood",
    "https://myanimelist.net/manga/2/Berserk"
  ],
  "maxReviews": 0
}
```

**Recurring incremental monitoring of one chart:**

```json
{
  "mode": "charts",
  "mediaType": "manga",
  "chartKind": "top",
  "maxItems": 100,
  "incrementalMode": true
}
```

A capped chart walk like this one reports NEW, UPDATED and REAPPEARED titles, but never EXPIRED ones: `emitExpired` only fires after a run that walked every page to the natural end, with no **Maximum records** or **Maximum pages** cap stopping it first.

A genuinely empty search is reported honestly as an empty result, never as a failure. Profile pages (user lists) are not scraped: they are account-scoped.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/myanimelist-scraper").call(run_input={"mode": "search", "searchTerms": ["Frieren"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item.get("score"))
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/myanimelist-scraper').call({ mode: 'search', searchTerms: ['Frieren'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips every record id it already collected, so you do not pay twice. A resumed run never marks anything `EXPIRED`.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same scope. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that walked every page of every scope to its natural end, and only with `emitExpired`). `stateKey` names or shares the stored state. Change detection compares every output field except the bookkeeping keys, the fields that are always left out (`reviews.reactionCount`, `reviews.reactions`, `malId`, `titleSynonyms`, `airedFrom`, `airedTo`, `season`, `year`, `trailerUrl` and `scrapedAt`) and the fields you list in `ignoreFieldsForChanges`; whole-number values compare equal whether written 25 or 25.0. With incremental mode off, output is exactly as before.
- **Ignore fields when detecting changes** (`ignoreFieldsForChanges`, incremental mode only) lists fields that must not count as a change, such as a fast-moving counter. Use dotted paths for nested fields (lists are searched element by element). The always-left-out fields listed above are ignored without you naming them. Ignored fields stay in the output; they are only left out of change detection and `changedFields`. Changing this list never causes a one-time wave of UPDATED rows.
- **One run shares one "already returned" list across all its keywords.** A keyword's walk treats a page made up only of titles this run already returned as the end of its results, once that keyword has returned at least one new title on an earlier page. So when keywords in the same `searchTerms` list overlap, a later keyword can stop early and skip its own later pages, even though those pages still hold titles no keyword has returned yet. This is rare for unrelated keywords, but likely for near-duplicate ones (for example `"Attack on Titan"` and `"Attack on Titan Season 2"` in the same run). With `incrementalMode` and `emitExpired` on, that early stop can also mark a title that is still on MyAnimeList as `EXPIRED`: if it sat only on a skipped page, the run counts it as gone. The same rule applies to a resumed run: if the ranking shifted since the interrupted run (common on the airing and most-popular charts), a page of already-collected titles can end the resumed walk before the pages it still needed.
- **Workaround for a guaranteed-complete scan:** put each near-duplicate keyword in its own run, and give each run its own incremental state (leave `stateKey` empty, or use a different `stateKey` per run). Never reuse one `stateKey` for runs with different keywords: each complete run would mark the other run's titles `EXPIRED`. If a resumed run looks short, start a fresh run instead.

### Send results into your apps (MCP connectors)

After a run, results can also be piped into your apps: `mcpConnectors` names the connector ids, `notionParentPageUrl` sets the Notion parent page, and `maxNotifyListings` caps items per connector (default 50, dataset unchanged). Connector export needs the connector authorized in Apify; the dataset is written regardless.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `"search"` | Keyword search, Ranking charts, or Paste URLs. |
| `mediaType` | string | `"anime"` | Anime, manga or both (search and charts; URL mode auto-detects). Anime and manga ids are separate numbering, so with `both` a manga row is kept even when an anime row has the same number. |
| `searchTerms` | array | `["Frieren"]` | Keywords; each is its own walk, merged and de-duplicated. |
| `searchType` | string | `""` | MyAnimeList's own type filter for keyword search (TV, OVA, movie, special, ONA, music for anime; manga, novel, one-shot, doujinshi, manhwa, manhua for manga). |
| `searchQueries` | array | none | Alias of `searchTerms`; both lists are merged. When an API call sets only `searchQueries`, `animeIds`, `mangaIds` or `topType`, the shipped sample term `Frieren` is not run next to it. |
| `chartKind` | string | `"top"` | top, airing, upcoming, bypopularity or favorite. |
| `topType` | string | `""` | Shortcut for the Top ranking chart of anime or manga (empty, `anime` or `manga`). |
| `topLimit` | integer | `0` | With `topType`: how many ranked titles. It sets the item cap while `maxItems` is left at its default of 20; any other `maxItems` value wins. 0 uses `maxItems`. |
| `urls` | array | one example per link shape | Links to read directly: detail, reviews, search, chart, genre, season and studio pages (see URL shapes). Default: one example of each shape; that untouched default list is not run next to `animeIds`, `mangaIds`, `searchQueries` or `topType`. |
| `animeIds` | array | none | MyAnimeList ids read as detail pages, alone or on top of the selected mode (counted by the same Maximum items cap). |
| `mangaIds` | array | none | MyAnimeList ids read as detail pages, alone or on top of the selected mode (counted by the same Maximum items cap). |
| `fetchDetails` | boolean | `true` | Read every record's own page (adds the detail-enrichment surcharge). |
| `maxReviews` | integer | `3` | Reviews per record: 3 previews by default, a larger finite limit, or 0 for all available pages up to 5000 reviews. Requires detail fetching; large histories take longer. |
| `maxItems` | integer | `20` | The one finite cap (default 20). 0 means no limit. |
| `maxPages` | integer | `0` | Pages per scope. 0 (default) walks to the natural end. |
| `proxy` | object | `{"useApifyProxy": true}` | Optional connection settings. |
| `resumeFromRunId` | string | none | Previous run or dataset id; the run returns only new records. |
| `incrementalMode` | boolean | `false` | Classify changes across scheduled runs of the same scope. |
| `stateKey` | string | none | Optional campaign name for the incremental baseline. |
| `emitUnchanged` | boolean | `false` | Also return unchanged records (bills extra rows). |
| `emitExpired` | boolean | `false` | Also return records no longer found, after a complete scan (bills extra rows). |
| `ignoreFieldsForChanges` | array | none | Fields (dotted paths for nested ones) left out of change detection; incremental mode only. |
| `mcpConnectors` | array | none | Connector ids to also export results into. |
| `notionParentPageUrl` | string | none | Parent page for the Notion export. |
| `maxNotifyListings` | integer | `50` | Maximum items per connector export. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

The dataset holds one record per row; the overview view shows the headline columns. Fictional sample:

```json
{
  "id": 123456,
  "mediaType": "anime",
  "title": "Sample Anime: Legend of the Test Fields",
  "type": "TV",
  "episodes": 12,
  "score": 8.61,
  "scoredBy": 4200,
  "ranked": 114,
  "members": 1234567,
  "reviewCount": 12,
  "reviews": [{"author": "sample_user", "date": "Jan 1, 2026", "recommendation": "Recommended", "rating": 9, "text": "An illustrative review preview."}],
  "reviewsFetched": 1,
  "reviewsComplete": false,
  "reviewPagesFetched": 0,
  "reviewCountGap": null,
  "characters": [{"id": 100001, "name": "Sample Character", "role": "Main", "voiceActors": [{"id": 200001, "name": "Sample Performer", "language": "Japanese"}]}],
  "voiceActors": [{"id": 200001, "name": "Sample Performer", "language": "Japanese", "characterId": 100001}],
  "relatedEntries": [{"id": 100002, "mediaType": "manga", "title": "Sample Source Manga", "relation": "Adaptation"}],
  "staff": [{"id": 200002, "name": "Sample Director", "role": "Director"}],
  "openingThemes": ["Sample Opening"],
  "endingThemes": ["Sample Ending"],
  "genres": ["Action", "Adventure"],
  "studios": ["Sample Studio"],
  "synopsis": "A sample synopsis body.",
  "url": "https://myanimelist.net/anime/123456/Sample_Anime",
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-01-01T00:00:00Z",
  "lastSeenAt": "2026-01-01T00:00:00Z"
}
```

### Plan Requirement

An Apify account is required to run this actor. Measured on this site: the standard Apify datacenter proxy reads MyAnimeList pages fine, so no residential proxy is needed and the default connection settings work on a plan that includes Apify proxy. If the default connection is refused for your account, the actor logs "The selected connection is unavailable to this run. Apify free plans do not include residential proxies." and continues on a backup connection; once that backup serves pages it logs "Served over the backup connection (the default connection was unavailable)." If nothing can be read the run fails with one of these messages:

- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required." (also check the credentials of any proxy URLs you entered).
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested MyAnimeList pages."
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

Each page request has a 25 second budget across retries, so a slow page cannot stall the run.

#### Pricing and billing

The actor uses pay per event. The **Pricing** tab shows the current rates; this page does not quote prices. Charged events and when they fire:

- **Result record:** one per record returned.
- **Detail enrichment:** one per returned record that was enriched with its own detail page (`fetchDetails` on).
- **Review enrichment:** one per review item returned in a record, including the three default previews.

Detail and review events are charged per record, right after the record is saved, not at the end of the run, so a stopped or timed-out run is billed only for the records already delivered. Records suppressed as UNCHANGED, failed pages and unread records are never charged. Turn `fetchDetails` off for listing fields only (no enrichment events), and use **Maximum records** to cap any run.

### FAQ

#### How much does it cost?

You pay for each record returned, plus the detail-enrichment and review-enrichment events only when they actually fire (a record you did not enrich, or one suppressed as UNCHANGED, is never charged for either). The **Pricing** tab on the actor page shows the current rates. Use **Maximum records** to cap the cost of any run.

#### Is it legal to scrape MyAnimeList?

This actor collects only publicly available anime and manga data. You are responsible for how you use it: follow MyAnimeList's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Titles, scores and rankings are largely factual catalogue data, but review text is authored by MyAnimeList users, and character or key art images may carry separate rights.

#### Can I get only new or updated titles on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then classifies every record against the last run's baseline for the same scope, and unchanged records are not billed unless you turn on **Emit unchanged records**. See "Resume and recurring updates" above for the one caveat: overlapping keywords in one run can end a keyword's walk early and, with **Emit expired records** on, mark a still-listed title EXPIRED. Put near-duplicate keywords in separate runs, each with its own state, and never reuse one `stateKey` across runs with different keywords.

#### Why did a search or chart return fewer titles than I expected?

Three separate reasons produce a short result, and only one of them is a cap: **Maximum records** or **Maximum pages per keyword or chart** stopped the walk on purpose; the search genuinely has no more matches (MyAnimeList answers a search with no more rows the same way it answers a search with no rows at all); or, for a run with more than one keyword in `searchTerms` or a resumed run, the shared "already returned" caveat above cut a walk short. Raise the caps, broaden the keyword, or split near-duplicate keywords into separate runs.

#### Why did my run fail instead of returning an empty dataset?

A run with no results fails, with a clear connection-problem message, only when not a single page could be read, so "no titles matched" is never confused with "nothing could be read". Run it again in a few minutes. A run also stops at the start, before anything is collected or charged, when the input cannot work: an unreadable `resumeFromRunId`, or `resumeFromRunId` combined with incremental mode on a scope that already has saved state. If only some keywords, charts or URLs could not be read, you still get what was found, with a status message naming how many.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/anilist-anime-manga-scraper"><b>AniList Anime &amp; Manga Scraper</b></a><br>Scrape AniList anime and manga: titles (romaji/English/native), scores, popularity...</td><td>🧩 <a href="https://apify.com/abotapi/webtoons-com-scraper"><b>WEBTOON Scraper</b></a><br>Scrape the WEBTOON catalog into structured data. Extract series, genres, rankings and...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/pinedrama-scraper"><b>PineDrama Scraper</b></a><br>Scrape Pine Drama series by keyword, category or URL across 17 languages. Extract titles...</td><td>🧩 <a href="https://apify.com/abotapi/goodshort-catalog-scraper"><b>GoodShort Scraper</b></a><br>Scrape the GoodShort short-drama catalog: titles with exact views, follows, genres and...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/shortmax-scraper"><b>ShortMax Scraper</b></a><br>Scrape the ShortMax short-drama catalog: titles, descriptions, creators, view and follow...</td><td>🧩 <a href="https://apify.com/abotapi/reelshort-com-scraper"><b>ReelShort Scraper</b></a><br>Scrape the ReelShort vertical short-drama catalog: titles, synopses, themes and full tag...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/myanimelist-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

Search MyAnimeList by keyword, walk a ranking chart, or read detail pages you paste.

## `mediaType` (type: `string`):

Applies to search and charts. 'Both' walks anime then manga. URL mode detects anime or manga from each URL.

## `searchTerms` (type: `array`):

One or more keywords. Each keyword is its own walk; results are de-duplicated across keywords.

## `searchType` (type: `string`):

MyAnimeList's own type filter for keyword search. A value that does not apply to the media type (for example TV on manga) is ignored for that media type.

## `searchQueries` (type: `array`):

Alias of Search keywords; both lists are merged.

## `chartKind` (type: `string`):

Which ranking chart to walk in Charts mode.

## `topType` (type: `string`):

Shortcut for Charts mode with the Top ranking of this media type. Use Top list limit to set how many ranked titles.

## `topLimit` (type: `integer`):

With Top list: number of ranked titles. It sets the item cap whenever Maximum items is left at its default of 20; a Maximum items value other than 20 wins. 0 = use Maximum items.

## `urls` (type: `array`):

Links to read: anime/manga detail pages (with or without slug), their reviews pages, keyword-search pages, top charts, genre, season and studio listings. The mobile host m.myanimelist.net is mapped to www. Unsupported links are skipped with a warning in the log. Left untouched, this holds one example of every supported shape (Maximum items still caps the total). That untouched default list is not run when Anime ids, Manga ids, Search queries or Top list are set.

## `animeIds` (type: `array`):

MyAnimeList anime ids (for example 5114). Read as detail pages, in addition to the selected mode (or alone when no mode is set). Anime and manga ids are separate numbering: anime 21 and manga 21 are different titles.

## `mangaIds` (type: `array`):

MyAnimeList manga ids (for example 2). Read as detail pages, in addition to the selected mode.

## `fetchDetails` (type: `boolean`):

Adds synopsis, full stats, related entries, character and staff previews, voice actors, and three review previews from each record's page. Use Maximum reviews to collect more. Adds a per-record detail-enrichment surcharge (see Output).

## `maxReviews` (type: `integer`):

3 (default) uses title-page previews with no extra review-page requests. Set a larger number (up to 5000) to collect that many reviews, or 0 for the most available review pages up to the same 5000-review ceiling. Applies only when details are fetched; each returned review, including a preview, adds one review-enrichment event.

## `maxItems` (type: `integer`):

Stop after this many records across all keywords or links. 0 means no limit: the run ends at the natural end of results.

## `maxPages` (type: `integer`):

Pages to walk per keyword or chart. 0 (default) keeps walking until the item cap or the natural end of results; Max records remains the only cap that stops a run early.

## `proxy` (type: `object`):

Use the default connection settings, or adjust them for your run.

## `resumeFromRunId` (type: `string`):

Paste a previous run id (or dataset id) to skip the records it already collected and return only new ones.

## `incrementalMode` (type: `boolean`):

For scheduled runs of the same scope: classify each record as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against the previous run's baseline. Compared: every output field of the record except the incremental bookkeeping keys (changeType, changedFields, firstSeenAt, lastSeenAt), the fields that are always left out (reviews.reactionCount, reviews.reactions, malId, titleSynonyms, airedFrom, airedTo, season, year, trailerUrl and scrapedAt; rank is compared: it is the chart position on chart rows and mirrors the overall score rank only on rows without a chart position) and any field listed in Ignore fields; whole-number values compare equal whether written 25 or 25.0. Excluded fields stay in the output. Records whose content did not change are suppressed unless Emit unchanged is on.

## `stateKey` (type: `string`):

Stable name for a monitoring campaign. Leave empty to key the baseline automatically on the mode, media type and scope.

## `emitUnchanged` (type: `boolean`):

With incremental mode on, also return records whose content did not change, marked UNCHANGED. These are extra rows: they are returned AND billed.

## `emitExpired` (type: `boolean`):

With incremental mode on, also return records no longer found, marked EXPIRED. Only after a complete uncapped scan of the scope; these are extra rows: they are returned AND billed.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode only: field names that must not count as a change, for example a fast-moving counter. Use dotted paths for nested fields (reviews.rating); lists are searched element by element. These fields are always left out already: reviews.reactionCount, reviews.reactions, malId, titleSynonyms, airedFrom, airedTo, season, year, trailerUrl and scrapedAt. rank is not among them: it is the chart position on chart rows and mirrors the overall score rank (ranked) only on rows without a chart position. The fields stay in the output, they are just left out of change detection and changedFields.

## `mcpConnectors` (type: `array`):

Connector ids to also export results into after the run. Dataset output is unchanged.

## `notionParentPageUrl` (type: `string`):

Parent page for the Notion connector export.

## `maxNotifyListings` (type: `integer`):

Maximum items sent to each connector. Does not change the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "mediaType": "anime",
  "searchTerms": [
    "Frieren"
  ],
  "searchType": "",
  "chartKind": "top",
  "topType": "",
  "topLimit": 0,
  "urls": [
    "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood",
    "https://myanimelist.net/anime/5114",
    "https://myanimelist.net/manga/2/Berserk",
    "https://myanimelist.net/manga/2",
    "https://myanimelist.net/anime.php?q=Frieren&cat=anime",
    "https://myanimelist.net/anime.php?cat=anime&q=Frieren&type=0",
    "https://myanimelist.net/anime.php?q=Frieren&cat=anime&show=0",
    "https://myanimelist.net/manga.php?q=berserk&cat=manga",
    "https://myanimelist.net/topanime.php",
    "https://myanimelist.net/topanime.php?type=airing&limit=50",
    "https://myanimelist.net/topanime.php?limit=50",
    "https://myanimelist.net/topmanga.php?type=bypopularity",
    "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood/reviews",
    "https://myanimelist.net/anime/5114/x/reviews?p=2",
    "https://myanimelist.net/manga/2/Berserk/reviews",
    "https://myanimelist.net/anime/genre/1/Action",
    "https://myanimelist.net/anime/season",
    "https://myanimelist.net/anime/season/2024/fall",
    "https://myanimelist.net/anime/producer/1/Studio_Pierrot"
  ],
  "fetchDetails": true,
  "maxReviews": 3,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "mediaType": "anime",
    "searchTerms": [
        "Frieren"
    ],
    "chartKind": "top",
    "urls": [
        "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood",
        "https://myanimelist.net/anime/5114",
        "https://myanimelist.net/manga/2/Berserk",
        "https://myanimelist.net/manga/2",
        "https://myanimelist.net/anime.php?q=Frieren&cat=anime",
        "https://myanimelist.net/anime.php?cat=anime&q=Frieren&type=0",
        "https://myanimelist.net/anime.php?q=Frieren&cat=anime&show=0",
        "https://myanimelist.net/manga.php?q=berserk&cat=manga",
        "https://myanimelist.net/topanime.php",
        "https://myanimelist.net/topanime.php?type=airing&limit=50",
        "https://myanimelist.net/topanime.php?limit=50",
        "https://myanimelist.net/topmanga.php?type=bypopularity",
        "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood/reviews",
        "https://myanimelist.net/anime/5114/x/reviews?p=2",
        "https://myanimelist.net/manga/2/Berserk/reviews",
        "https://myanimelist.net/anime/genre/1/Action",
        "https://myanimelist.net/anime/season",
        "https://myanimelist.net/anime/season/2024/fall",
        "https://myanimelist.net/anime/producer/1/Studio_Pierrot"
    ],
    "fetchDetails": true,
    "maxReviews": 3,
    "maxItems": 20,
    "maxPages": 0,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "maxNotifyListings": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/myanimelist-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "mediaType": "anime",
    "searchTerms": ["Frieren"],
    "chartKind": "top",
    "urls": [
        "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood",
        "https://myanimelist.net/anime/5114",
        "https://myanimelist.net/manga/2/Berserk",
        "https://myanimelist.net/manga/2",
        "https://myanimelist.net/anime.php?q=Frieren&cat=anime",
        "https://myanimelist.net/anime.php?cat=anime&q=Frieren&type=0",
        "https://myanimelist.net/anime.php?q=Frieren&cat=anime&show=0",
        "https://myanimelist.net/manga.php?q=berserk&cat=manga",
        "https://myanimelist.net/topanime.php",
        "https://myanimelist.net/topanime.php?type=airing&limit=50",
        "https://myanimelist.net/topanime.php?limit=50",
        "https://myanimelist.net/topmanga.php?type=bypopularity",
        "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood/reviews",
        "https://myanimelist.net/anime/5114/x/reviews?p=2",
        "https://myanimelist.net/manga/2/Berserk/reviews",
        "https://myanimelist.net/anime/genre/1/Action",
        "https://myanimelist.net/anime/season",
        "https://myanimelist.net/anime/season/2024/fall",
        "https://myanimelist.net/anime/producer/1/Studio_Pierrot",
    ],
    "fetchDetails": True,
    "maxReviews": 3,
    "maxItems": 20,
    "maxPages": 0,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "maxNotifyListings": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/myanimelist-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "mediaType": "anime",
  "searchTerms": [
    "Frieren"
  ],
  "chartKind": "top",
  "urls": [
    "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood",
    "https://myanimelist.net/anime/5114",
    "https://myanimelist.net/manga/2/Berserk",
    "https://myanimelist.net/manga/2",
    "https://myanimelist.net/anime.php?q=Frieren&cat=anime",
    "https://myanimelist.net/anime.php?cat=anime&q=Frieren&type=0",
    "https://myanimelist.net/anime.php?q=Frieren&cat=anime&show=0",
    "https://myanimelist.net/manga.php?q=berserk&cat=manga",
    "https://myanimelist.net/topanime.php",
    "https://myanimelist.net/topanime.php?type=airing&limit=50",
    "https://myanimelist.net/topanime.php?limit=50",
    "https://myanimelist.net/topmanga.php?type=bypopularity",
    "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood/reviews",
    "https://myanimelist.net/anime/5114/x/reviews?p=2",
    "https://myanimelist.net/manga/2/Berserk/reviews",
    "https://myanimelist.net/anime/genre/1/Action",
    "https://myanimelist.net/anime/season",
    "https://myanimelist.net/anime/season/2024/fall",
    "https://myanimelist.net/anime/producer/1/Studio_Pierrot"
  ],
  "fetchDetails": true,
  "maxReviews": 3,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}' |
apify call abotapi/myanimelist-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/myanimelist-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/W66LQWzgpo4FronDe/builds/ViCEhzoGxQ4bgJ1OH/openapi.json
