需要 PHP 8.4.4 或更高版本,v4 及更早版本已停止支持;本仓库只是 PHP 库,REST API 需另装 jikan-me/jikan-rest 且官方不提供托管实例;不支持认证请求,无法更新用户的 MyAnimeList 列表

jikan
jikan-meMIT活跃维护非官方 MyAnimeList PHP 库兼 REST API,能抓取官方接口不提供的番剧与漫画数据,方便开发者直接调用。
Unofficial MyAnimeList PHP+REST API which provides functions other than the official API
composer require jikan-me/jikan "^5.0"README 未说明;这是 PHP 库,不涉及 GPU 或显存
用 PHP 抓取 MyAnimeList 网页数据的库,补上官方 API 缺失的功能
非官方 MyAnimeList PHP 库兼 REST API,能抓取官方接口不提供的番剧与漫画数据,方便开发者直接调用。
核心能力
- 通过抓取 MyAnimeList 网页获取官方 API 没有的数据
- 每个公开端点对应一个 MalClient 方法和一个 Request 值对象
- 提供动漫详情与动漫搜索等请求类
- 自带基于录制 fixture 的 hermetic 测试与质量门禁
- 配套独立的可自托管 REST API 项目 jikan-rest
容易踩的坑
- v5.0.0Jikan v5.0.0最新正式版2026-10-19 · 2026-10-19 17:06 UTC
The first stable release of the
^5line. The code is identical to5.0.0-rc.2. jikan-rest validated it on its Laravel 13 migration branch (403 tests, a 114-route crawl, and the news and "recently updated" endpoints that rc.1 had broken), and a live check of 14 MyAnimeList pages found no markup drift.Upgrading from v4? Read UPGRADING.md. It lists every change to your code, and was checked by running v4 and v5 against the same 85 recorded pages. The sections below it record each pre-release in full.
Breaking (from v4)
- PHP 8.4.4 or later — you must run it; Composer will not install on older versions. PHP 8.4.0–8.4.3 have an HTML5 parser bug that corrupts some emoji and rare characters. (#642)
- The HTTP layer is Symfony's instead of
fabpot/goutte— you must runcomposer update.new MalClient($client)still takes aSymfony\Contracts\HttpClient\HttpClientInterface. (#607) - Removed:
AnimeCard::getLicensors()andSeasonalAnime::getLicensors()(readAnime::getLicensors()),Model\Manga\MangaReviewer,Parser\Manga\MangaReviewerParser,Parser\Manga\MangaReviewScoresParser,Jikan\Goutte\GoutteWrapper(nowJikan\Http\HttpClientWrapper), andNewsListItemParser::getImage()andgetIntro(). (#643) - Many getters can return
nullwhere a missing field used to be aTypeError— you must handlenull. The list is inUPGRADING.md§3. (#658) getNewsList()returnsModel\News\ResourceNewsList; the getters have the same names. (#630)Constants::SEARCH_MANGA_NOVELis now8(it was2, which MyAnimeList reads as Light Novel); useSEARCH_MANGA_LIGHT_NOVELfor2. (#653)
Added
- 11 content endpoints:
getArticle,getArticleSearch,getArticleTags,getArticlesByTag,getNews,getNewsByTag,getNewsSearch,getNewsTags,getPinnedArticles,getRecentArticlesandgetRecentNews, each with itsJikan\Request\*class. (#607) - Search constants for the anime types CM, PV and TV Special, the manga type Light Novel, and the manga statuses On Hiatus and Discontinued. (#653)
Jikan\Helper\Time: relative and partial dates resolve against a PSR-20 clock (the system clock by default; pin it in tests withSymfony\Component\Clock\Clock::set()). (#643)
Changed
- Anime-card start dates (
getAiringStart()on genre, producer, seasonal and schedule cards) are UTC midnight of the day shown, likeAnime::getAired()and the episode dates. They were midnight in Japan converted to UTC, so "Oct 2, 2026" gave2026-10-01T15:00:00+00:00and a year-only "2027" gave2026-12-31T15:00:00+00:00, the previous year. The instant is 9 hours later; if you read the date part, it is now the day shown. Text that carries a broadcast time still converts from Japan time. (#666) AnimeEpisode::getAired()(fromgetAnimeEpisode()) is UTC midnight of the printed day, like the episode list andAnime::getAired(). It was midnight in Japan (1999-10-20T00:00:00+09:00), because the page prints "Aired: Oct 20, 1999(JST)" and PHP read the "(JST)" as a UTC+9 offset, so the same episode had two different dates depending on the endpoint (jikan-rest#567). The calendar date is unchanged; the instant is 9 hours later. If you compared timestamps between the two endpoints, they are now equal. A new check in the snapshot tests fails on any date that is not UTC. (#664)getHelpfulCount()on anime
- v5.0.0-rc.2Jikan v5.0.0-rc.2pre-release2026-10-15 · 2026-10-15 19:53 UTC
查看更新说明
Release candidate 2. Bug fixes only since
5.0.0-rc.1; the API is unchanged. jikan-rest found both problems while validating rc.1: three of its endpoints (/anime/{id}/news,/manga/{id}/newsand/manga/{id}/userupdates) came back empty.Fixed
getNewsList()returns the articles on an anime's or manga's news page again (AnimeNewsRequest,MangaNewsRequest; jikan#661). Those pages have their own layout (nodiv.news-unit), which the parser stopped reading when it was realigned to the main news index in v5.0.0-beta, so it returned an empty list. Each article has its title,/news/{id}URL, date, author, discussion link, comment count, excerpt and image, with absolute links. Anything fetched through these requests since v5.0.0-beta was empty.getNewsList()pagination:getLastVisiblePage()was always1, even whenhasNextPage()wastrue. It is the furthest page the links reach. AndhasNextPage()no longer counts the "News" tab, which links to the page you're on, so the last page of a long news list no longer claims a next page.- "Recently updated by users" for anime and manga (
getAnimeRecentlyUpdatedByUsers(),getMangaRecentlyUpdatedByUsers(); jikan#663):- Crashes on a bare
-. MyAnimeList prints a progress cell as just-for many users (39 of 75 rows on manga 2). The total getters assumed aread/totalpair and raisedUndefined array key 1; where warnings become exceptions (Laravel does), a catch-all ingetResults()then returned no rows at all for the page. Those cells now read as unknown. - An unknown total is
null, not0.5/?(the series is still running) gave a total of0; it isnull. If you treated0as "unknown", check fornull. - A row that cannot be parsed is an error, reported as
ParserException, instead of an empty page next to ahasNextPage()that could betrue. A page without the table still has no rows.
- Crashes on a bare
Full changelog: https://github.com/jikan-me/jikan/compare/v5.0.0-rc.1...v5.0.0-rc.2
- v5.0.0-rc.1Jikan v5.0.0-rc.1pre-release2026-10-15 · 2026-10-15 18:10 UTC
查看更新说明
Release candidate. The v5 API is frozen: from here until
5.0.0only bug fixes land, and anything that would change a signature waits for the next major. Upgrading from v4? Read UPGRADING.md. Compared with5.0.0-beta.3, three values changed, all listed below.Changed
- Anime-card start dates (
getAiringStart()on genre, producer, seasonal and schedule cards) are UTC midnight of the day shown, likeAnime::getAired()and the episode dates. They were midnight in Japan converted to UTC, so "Oct 2, 2026" gave2026-10-01T15:00:00+00:00and a year-only "2027" gave2026-12-31T15:00:00+00:00, the previous year. The instant is 9 hours later; if you read the date part, it is now the day shown. Text that carries a broadcast time still converts from Japan time. AnimeEpisode::getAired()(fromgetAnimeEpisode()) is UTC midnight of the printed day, like the episode list andAnime::getAired(). It was midnight in Japan (1999-10-20T00:00:00+09:00), because the page prints "Aired: Oct 20, 1999(JST)" and PHP read the "(JST)" as a UTC+9 offset, so the same episode had two different dates depending on the endpoint (jikan-rest#567). The calendar date is unchanged; the instant is 9 hours later. If you compared timestamps between the two endpoints, they are now equal. A new check in the snapshot tests fails on any date that is not UTC.getHelpfulCount()on anime and manga reviews returns the review's total reactions. MyAnimeList's 2022 review redesign replaced "helpful" votes with reactions (Nice, Love it, Funny, Confusing, Informative, Well-written, Creative), and this is their total. It counts every reaction, not only approving ones; usegetReactions()for the breakdown. Before this, the anime version always returned0and the manga version failed looking for the removed vote count.
Full changelog: https://github.com/jikan-me/jikan/compare/v5.0.0-beta.3...v5.0.0-rc.1
- Anime-card start dates (
- v5.0.0-beta.3Jikan v5.0.0-beta.3pre-release2026-10-15 · 2026-10-15 15:44 UTC
查看更新说明
Pre-release, and the last planned API change before the release candidate. Read Breaking before upgrading: 31 getters that could throw a
TypeErrornow returnnull, andAnimeCard::getMembers()returnsnullinstead of throwing.Breaking
-
31 model getters can now return
null, where they used to throw aTypeErroron pages that don't show the field. You must handlenullfrom these getters; code that only ran on pages with the field present sees no difference.Anime::getDuration()EpisodeListItem::getScore(),getUrl(),getForumUrl()StreamEpisodeListItem::getMalId()ResourceArticle::getDate()NewsMeta::getDate(),getMalId()NewsListItem::getDate(),ResourceNews::getDate(),ResourceNewsListItem::getDate()getMalId()onAnimeGenreListItem,MangaGenreListItem,MagazineListItemandProducerListItemPerson::getMemberFavorites()VoiceActingRole::getRole()UserSearchListItem::getLastOnline()AnimeStats:getDaysWatched(),getEpisodesWatched(),getMeanScore(),getRewatched(),getTotalEntries()MangaStats:getChaptersRead(),getDaysRead(),getMeanScore(),getReread(),getTotalEntries(),getVolumesRead()Profile::getLocation()UserReviews::getLastVisiblePage()
Each was declared non-null while its parser already returned
nullwhen MyAnimeList leaves the field out, for example an episode nobody has voted on, or a profile without a location. -
AnimeCard::getMembers()returns?int:nullwhen a card shows no member count. In beta.2 it raisedParserException, which failed the whole page. You must handlenull.
Fixed
getUserReviews()pagination:hasNextPage()was alwaystrueandgetLastVisiblePage()always1, so paging through a user's reviews never ended. MyAnimeList shows 10 reviews per page and links only the next page ("More Reviews"), sohasNextPage()now reports that link andgetLastVisiblePage()is the next page when there is one.getAnimeScores()andgetMangaScores()on anime reviews, manga reviews and reviewers return the reviewer's overall rating. They always failed: the anime one called a parser that had been deleted, and the manga one read a score table that no longer exists. MyAnimeList's 2022 review redesign dropped the per-category ratings, sogetOverall()is the "Reviewer's Rating" and the category getters (getStory(),getAnimation(),getArt()and so on) returnnull.AnimeReviewScores/MangaReviewScoresare built withfromOverall(); theirfromParser()andJikan\Parser\Manga\MangaReviewScoresParser, which could not work, are removed.
Full changelog: https://github.com/jikan-me/jikan/compare/v5.0.0-beta.2...v5.0.0-beta.3
-
- v5.0.0-beta.2Jikan v5.0.0-beta.2pre-release2026-10-15 · 2026-10-15 09:49 UTC
查看更新说明
Pre-release. Hardening and fidelity work on the
^5line sincev5.0.0-beta: parser fixes found by a recorded-page test corpus and mutation testing, plus fields MyAnimeList serves that were read wrong or not at all. Read Breaking and Changed before upgrading: the PHP floor is now 8.4.4,AnimeCard::getLicensors()is gone, andSEARCH_MANGA_NOVELchanged value.Breaking
AnimeCard::getLicensors()is removed, and soSeasonalAnime::getLicensors()too — you must stop calling it. Anime cards on genre, producer and seasonal lists no longer show licensors on MyAnimeList, and the method has returned an empty list since. Licensors are still on the anime itself:Anime::getLicensors()fromgetAnime().- PHP floor raised to 8.4.4 — you must run PHP 8.4.4 or later; Composer no longer installs on 8.4.0–8.4.3. Those patch levels have a bug in PHP's own HTML5 parser (
Dom\HTMLDocument, php-src GH-17481) that corrupts some 4-byte characters (emoji, rare scripts) depending on where they fall in the page, so a parsed field can contain invalid UTF-8 andjson_encode()fails on the model. Seen on a real MyAnimeList manga review.
Added
- Search filter constants for the options MyAnimeList's search form has gained:
- Anime types
SEARCH_ANIME_CM(7),SEARCH_ANIME_PV(8) andSEARCH_ANIME_TV_SPECIAL(9). - Manga type
SEARCH_MANGA_LIGHT_NOVEL(2). - Manga statuses
SEARCH_MANGA_STATUS_ON_HIATUS(4) andSEARCH_MANGA_STATUS_DISCONTINUED(5).
- Anime types
Jikan\Helper\Timeresolves MyAnimeList's relative and partial dates ("Yesterday, 9:25 PM", "5 hours ago", "Oct 13", weekdays) against a PSR-20 clock from the newsymfony/clockdependency. By default that is the system clock, so results are unchanged. You can pin "now", for example in your own tests, withSymfony\Component\Clock\Clock::set(new MockClock(...)).
Changed
Constants::SEARCH_MANGA_NOVELis now8(was2). MyAnimeList's manga search now reads2as Light Novel and8as Novel, so this constant was searching for light novels. If you want light novels, use the newConstants::SEARCH_MANGA_LIGHT_NOVEL(2).symfony/dom-crawleris now a declared requirement (^7.4 || ^8.0). The library has always used it directly but only received it throughsymfony/browser-kit, so nothing changes in your tree — no action needed unless you were excluding it.- A seasonal page parses roughly 3x faster (about 1.2 s down to 0.4 s in our benchmark). The marker for "continuing" shows was found by reading the text of the whole season section once per anime card; it is now read once per section. The returned
Seasonalmodel is identical. - An anime card with no member count now raises
ParserExceptionfromMalClient(aRuntimeExceptionfromAnimeCardParser::getMembers()), where it used to raise an uncaught\TypeErrorthatcatch (\Exception)andcatch (ParserException)both missed. If you caught\TypeErroraround client calls to work around this, catchParserExceptioninstead. Jikan\Model\Manga\MangaReviewerandJikan\Parser\Manga\MangaReviewerParserare removed. They extended classes that were deleted earlier, so loading either one was a fatal error and nothing referenced them; no working code can have depended on them.Jikan\Model\User\Friend::getFriendsSince()returns?\DateTimeImmutable:nullwhen MyAnimeList shows no "Friends since" date. It used to declare a non-null date and throw aTypeErrorin that case.
Fixed
- Anime cards (genre, producer, seasonal and schedule pages;
AnimeCard/SeasonalAnime) now read their type and start date from fields MyAnimeList puts on every card. This fixes four problems:- Year-only start dates:
getAiringStart()gave today's date for a card that shows only a year ("2027"), because the year was read as the time 20:27 today. It now gives 1 January of that year. - Schedule page types:
getType()gave the day of the week ("Friday", "Other") inst
- Year-only start dates:
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Jikan - Unofficial MyAnimeList.net PHP API
Jikan is a PHP API for MyAnimeList.net. It scrapes the website to satisfy the need for API functionality that MyAnimeList.net lacks.
The raison d'être of Jikan is to assist developers easily get the data they need for their apps and projects without having to depend on the lackluster official API, unstable APIs, or sidetracking their projects to develop parsers.
The word Jikan literally translates to Time in Japanese (時間). And that's what this API saves you of. ;)
Notice: Jikan does not support authenticated requests. You can not update your lists.
Status: active development is on master — the v5 line, currently 5.0.0. The v4 line
is end of life and no longer supported. Jikan is a PHP library, not a hosted service; a
self-hosted REST API exists as a separate project.
Getting Started
| Version | Support | PHP | Lumen/Laravel |
|---|---|---|---|
^5 | ✅ Current — actively maintained | ^13 | |
^4 | ❌ End of life — no longer supported | ^9,^10 | |
^3 | ❌ No longer maintained or supported | ^8, ^9 | |
^2 | ❌ No longer maintained or supported | ^6 | |
~1 | ❌ No longer maintained or supported | 5.5.* |
composer require jikan-me/jikan "^5.0"(needs PHP 8.4.4 or later)- Coming from v4? Read the upgrade guide.
- Read the Repository docs below — this repo holds the canonical documentation.
Repository docs
- Contributing — the endpoint spine, hermetic-fixture workflow, pre-commit gates
- Changelog — what changed per version, written for consumers
- Upgrading from v4 — what to change in your code, checked against a side-by-side run of both versions
- Decision log — why things are the way they are
- Deprecations — the running deprecation ledger
- API standard — conventions every public entry point must follow
Usage
use Jikan\MyAnimeList\MalClient;
use Jikan\Request\Anime\AnimeRequest;
use Jikan\Request\Search\AnimeSearchRequest;
$client = new MalClient(); // or new MalClient(HttpClientInterface $http) to inject the client
$anime = $client->getAnime(new AnimeRequest(1)); // Cowboy Bebop
$results = $client->getAnimeSearch(new AnimeSearchRequest())->getResults();
Every public endpoint is one MalClient method taking one Jikan\Request\* value object; the
method list, the class-per-endpoint surface, and the conventions each must follow are documented in
readme (conceptually), API-STANDARD.md (requirements) and CONTRIBUTING.md §3 (how to add one).
The full machine-readable catalog lives in llm.txt (and .backend-suite/endpoints.json) — derived,
regenerated with composer endpoints:write. On every change that touches the surface, run
composer endpoints:write and composer endpoints:check.
REST API (self-hosted)
The library powers a separate, self-hostable REST API project: jikan-me/jikan-rest.
No hosted/public instance of the REST API is provided.
Wrappers
| Language | Wrappers |
|---|---|
| JavaScript | JikanJS by Zuritor |
| Java | Jikan4java by Doomsdayrsreactive-jikan by Sandro MarquesJaikan by ShindouMihou |
| Python | JikanPy by Abhinav Kasamsetty |
| Deno | Jikan.js by RPDJF |
| Node.js | jikan-node by xy137jikan-nodejs by ribeirogab |
| TypeScript | jikants by Julien Broyardjikan-client by Javier Blanco |
| PHP | jikan-php by Jan Vernieuwe |
| .NET | Jikan.net by Ervie |
| Go | jikan-go by Daren Liangjikan2go by nokusukun |
| Ruby | Jikan.rb by Zerocchi |
| Dart | jikan-dart by Rafal Wachol |
| Kotlin | JikanKt by Ganedra Afrasya |
Running Tests
composer quality # everything at once: php-cs-fixer dry-run + PHPStan + endpoint-manifest check + tests
composer test # PHPUnit via paratest — hermetic against recorded fixtures
Quality gates run automatically on every commit (grumphp: php-cs-fixer, php-parallel-lint,
git blacklist, paratest; plus the backend-suite error_handling/test_network_coupling
audits). The same composer quality gate is enforced in CI. Full workflow:
CONTRIBUTING.md.
Backers
A huge thank you to all our Patrons! 🙏 This project wouldn't be running without your support.
If you'd like to support the project, you can become a Patron!
Sugoi (すごい) Patrons
Patrons
- Aaron Treinish
- Abdelhafid Achtaou
- Aika Fujiwara
- Bobby Williams
- Cesar Irad Mendoza
- CrafterSama
- Fro116
- Jason Weatherly
- Jesse
- Kundan Chintamaneni
- Kururin
- Purplepinapples
- Ryo Ando
- Sakamotodesu
- TeraNovaLP
Development
![]() | Jikan's development is powered by JetBrain's Open Source License |
A shoutout to their amazing products and for supporting Jikan since early versions!
DISCLAIMER
- Jikan is not affiliated with MyAnimeList.net
- You are responsible for the usage of this API. Please be respectful towards MyAnimeList's Terms Of Service

