# AniList Scraper: Anime, Manga & Studio Data (`arman-bd/anilist-anime-scraper`) Actor

Scrape AniList via GraphQL: anime and manga titles, scores, popularity, genres, studios, staff, airing schedules and relations. Rich metadata for media apps.

- **URL**: https://apify.com/arman-bd/anilist-anime-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Automation, AI, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 title scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## AniList Scraper: Anime, Manga & Studio Data

![AniList Scraper — Anime and manga metadata — scores, popularity, genres, studios and airing dates](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/anilist-anime-scraper.jpg)

**AniList Scraper** pulls structured anime and manga metadata straight from the **AniList GraphQL API**, titles in three scripts, scores, popularity, favourites, genres, community tags, studios, airing dates and full synopses.

Give it a list of search terms, or skip search entirely and browse a whole season by genre. Either way you get one clean dataset row per title. **No proxy setup, no browser, no credentials to manage.**

### What you get

| Output field | Meaning |
|---|---|
| `id` | AniList media ID, stable, use it as your join key |
| `titleRomaji`, `titleEnglish`, `titleNative` | All three official title forms. `titleEnglish` is `null` when AniList has no licensed English title |
| `type` | `ANIME` or `MANGA` |
| `format` | `TV`, `MOVIE`, `OVA`, `ONA`, `SPECIAL`, `MANGA`, `NOVEL`, `ONE_SHOT` |
| `status` | `FINISHED`, `RELEASING`, `NOT_YET_RELEASED`, `CANCELLED`, `HIATUS` |
| `episodes`, `chapters` | Episode count for anime, chapter count for manga, the other is `null` |
| `averageScore` | Weighted community score out of 100 |
| `popularity`, `favourites` | Users with the title on a list, and users who favourited it |
| `genres` | AniList's curated genre list |
| `tags` | Community tags, ranked most-relevant first |
| `studios` | Producing studios, main studio first |
| `startDate`, `endDate` | `YYYY-MM-DD`, degrading to `YYYY-MM` or `YYYY` when AniList only knows part of the date |
| `season` | Airing season and year, e.g. `SPRING 2013` |
| `coverImage` | Highest-resolution cover art URL |
| `description` | Synopsis as clean plain text, AniList's stray `<br>` and `<i>` tags stripped |
| `scrapedAt` | Run timestamp |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, per-source counts, the filters applied, the number of API requests spent and any query that failed.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `searchTerms` | array | `[]` | Title searches, one query each, ranked by relevance. Empty = browse using the filters below. |
| `mediaType` | string | `ANIME` | `ANIME` or `MANGA`. |
| `season` | string | - | `WINTER`, `SPRING`, `SUMMER` or `FALL`. Anime only. |
| `seasonYear` | integer | - | Airing year, e.g. `2024`. |
| `genres` | array | `[]` | Exact AniList genre names. A title must carry **every** genre listed. |
| `maxResults` | integer | `100` | Total titles saved across the run. Split evenly between search terms. |

**Which combinations make sense**

- `searchTerms` alone, the fastest way to enrich a known list of titles.
- `season` + `seasonYear` with no `searchTerms`, the seasonal chart, most popular first. This is the mode to schedule.
- `genres` narrows either mode. Genres are ANDed, so `["Action", "Fantasy"]` returns only titles carrying both.
- `season` without `seasonYear` matches that season in *every* year, usually not what you want.

**Example input**

```json
{
 "searchTerms": ["attack on titan", "cowboy bebop"],
 "mediaType": "ANIME",
 "genres": ["Action"],
 "maxResults": 12
}
```

### Output

A real record from the run above, abridged in the long fields:

```json
{
 "id": 16498,
 "titleRomaji": "Shingeki no Kyojin",
 "titleEnglish": "Attack on Titan",
 "titleNative": "進撃の巨人",
 "type": "ANIME",
 "format": "TV",
 "status": "FINISHED",
 "episodes": 25,
 "chapters": null,
 "averageScore": 85,
 "popularity": 1038684,
 "favourites": 85824,
 "genres": ["Action", "Drama", "Fantasy", "Mystery"],
 "tags": ["Kaiju", "Revenge", "Tragedy", "Military", "Henshin", "…"],
 "studios": ["WIT STUDIO", "Pony Canyon", "Kodansha", "Production I.G", "…"],
 "startDate": "2013-04-07",
 "endDate": "2013-09-28",
 "season": "SPRING 2013",
 "coverImage": "https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx16498-buvcRTBx4NSm.jpg",
 "description": "Several hundred years ago, humans were nearly exterminated by titans. …",
 "scrapedAt": "2026-08-06T11:29:28.483Z"
}
```

`RUN_SUMMARY`:

```json
{
 "sourcesRequested": 2,
 "sourcesFailed": 0,
 "failures": [],
 "titlesSaved": 9,
 "titlesPerSource": { "attack on titan": 6, "cowboy bebop": 3 },
 "apiRequests": 2,
 "rateLimitPerMin": 30,
 "filters": {
 "searchTerms": ["attack on titan", "cowboy bebop"],
 "mediaType": "ANIME",
 "season": null,
 "seasonYear": null,
 "genres": ["Action"],
 "maxResults": 12
 },
 "finishedAt": "2026-08-06T11:29:30.148Z"
}
```

### Use cases

**1. Build an anime tracking app.** Seed your catalogue with the current season, then re-run weekly to pick up score and episode-count changes.

```json
{
 "mediaType": "ANIME",
 "season": "SPRING",
 "seasonYear": 2024,
 "maxResults": 300
}
```

**2. Analyse seasonal release trends.** Run the same query across seasons and compare studio output, genre mix and average scores. Change `season`/`seasonYear` per run and diff on `id`.

```json
{
 "mediaType": "ANIME",
 "season": "FALL",
 "seasonYear": 2024,
 "genres": ["Action"],
 "maxResults": 200
}
```

**3. Media licensing research.** Pull manga by genre with chapter counts, status and publisher studios to find completed series with high favourites-to-popularity ratios.

```json
{
 "mediaType": "MANGA",
 "genres": ["Psychological", "Thriller"],
 "maxResults": 150
}
```

### Limits and behaviour

- **GraphQL POST, fixed query.** Every request asks for the same field set, so 50 titles cost exactly one request regardless of filters.
- **Self-pacing rate limiter.** AniList advertises its budget in rate-limit headers and the Actor reads it from the first response, then spaces requests to match. It currently advertises **30 requests/minute**; AniList's documentation still describes 90/min, so the Actor trusts the header rather than the docs. A `429` is honoured via `retry-after`.
- **Null filters are omitted, not sent.** AniList applies an explicit `null` variable as a literal filter, `season: null` matches only titles with no season and returns nothing. Unset filters are dropped from the variables object entirely.
- **`maxResults` is a total, split evenly.** With two search terms and `maxResults: 12`, each term gets a budget of 6. A term that has fewer matches simply returns fewer; the leftover is not redistributed.
- **AniList caps `pageInfo.total` at 5000** for broad browse queries, so treat it as "at least 5000", not an exact count. Page size is capped at 50 server-side.
- **A failing query never aborts the run.** It is logged, recorded in `RUN_SUMMARY.failures`, and the next source proceeds. The Actor only errors out if *every* source fails.
- **Transient errors are retried.** 429 and 5xx get three attempts with linear backoff. GraphQL validation errors fail fast, because retrying a malformed query never helps.
- **Public data only.** No authentication, no personal data, no access-control bypass. AniList's content is user-contributed and licensed for reuse.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need an AniList account or API key?** No. You supply no credentials.

**What happens if AniList is unavailable?** The failing query is reported in `RUN_SUMMARY.failures` and the run continues with the next source. The run only fails outright if every source failed.

**Can I schedule it?** Yes, it is designed for scheduled runs. Weekly seasonal sweeps are the most common pattern; diff on `id` plus `averageScore` to catch score drift.

**Why is `titleEnglish` sometimes null?** AniList only carries an English title when one has been officially licensed. Fall back to `titleRomaji`, which is always present.

**Why did a search return fewer titles than I asked for?** Either the search has few matches, or your `genres` filter excluded the rest, genres are ANDed. `RUN_SUMMARY.titlesPerSource` shows the count for each term so you can tell the two apart.

**Can I get staff and character data?** Not in this Actor. The query shape is deliberately fixed so per-title cost stays constant; staff and character edges would multiply request volume per title.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~anilist-anime-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "mediaType": "ANIME",
 "season": "SPRING",
 "seasonYear": 2024,
 "maxResults": 75
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/anilist-anime-scraper').call({
 searchTerms: ['frieren', 'chainsaw man'],
 mediaType: 'ANIME',
 maxResults: 20,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const t of items) console.log(`${t.titleEnglish ?? t.titleRomaji}, ${t.averageScore} (${t.season})`);
```

# Actor input Schema

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

Title searches, one per entry. Each term runs as its own AniList query, and results are ranked by search relevance. Leave empty to browse by the filters below instead.

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

Which AniList catalogue to query. ANIME returns episode counts, MANGA returns chapter counts.

## `season` (type: `string`):

Restrict to an airing season. Anime only. manga has no season. Pair with a season year, otherwise AniList matches that season across every year.

## `seasonYear` (type: `integer`):

Restrict to titles from this airing year, for example 2024. Leave empty for all years.

## `genres` (type: `array`):

Keep only titles carrying every one of these AniList genres. Exact names required. Action, Adventure, Comedy, Drama, Ecchi, Fantasy, Horror, Mahou Shoujo, Mecha, Music, Mystery, Psychological, Romance, Sci-Fi, Slice of Life, Sports, Supernatural, Thriller.

## `maxResults` (type: `integer`):

Cap the total number of titles saved across the whole run. The budget is split evenly between search terms so a later term is never starved.

## Actor input object example

```json
{
  "searchTerms": [
    "frieren",
    "one piece"
  ],
  "mediaType": "ANIME",
  "season": "",
  "seasonYear": 2024,
  "genres": [
    "Action",
    "Fantasy"
  ],
  "maxResults": 100
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "searchTerms": [
        "attack on titan",
        "cowboy bebop"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/anilist-anime-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 = { "searchTerms": [
        "attack on titan",
        "cowboy bebop",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/anilist-anime-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "attack on titan",
    "cowboy bebop"
  ]
}' |
apify call arman-bd/anilist-anime-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=arman-bd/anilist-anime-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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