Metacritic Scores Scraper
Pricing
from $1.26 / 1,000 results
Metacritic Scores Scraper
Critic and user scores for games, movies and TV from Metacritic's own JSON API. Paginates by offset because Metacritic's `page` parameter is inert — every value returns the same 24 rows — and keeps the 0-100 critic score and the 0-10 user score clearly apart.
Pricing
from $1.26 / 1,000 results
Rating
0.0
(0)
Developer
Ibnu Adzim
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Share
Critic and user scores for games, movies and TV from Metacritic's own internal JSON API, plus full title detail from JSON-LD. HTTP-only, no API key, no login, no browser.
Modes
| Mode | What you get |
|---|---|
browse | The catalogue, sorted and filtered — Metascore, user score, sentiment breakdown, genres, release date. Games (14,335), movies (17,317) and TV (3,444). |
titles | Full detail for titles you name — cast, director/creator, publisher, platforms, images, trailer, description. |
Five upstream quirks it corrects
1. The parameter called page does nothing
Holding everything else constant:
page=1, 2, 3, 100, 600, 5000 -> the IDENTICAL 24 items, every time24/24 overlapping page 1, same first title,HTTP 200, plausible dataoffset=0, 24, 48, 240 -> 24 items each, ZERO overlap
This is worse than a clamp. A clamp eventually stops; page looks like it is
working forever. A walker built on it collects the same 24 rows over and over
and reports thousands of results — all duplicates — with no error and no empty
page to stop on.
The API tells on itself if you read links.next.href: its own "next" link
keeps page=1 and appends &offset=24. This actor paginates by offset,
never sends page, and publishes offsetsWalked instead of page numbers so
the walk is auditable.
2. The two scores are on different scales, and only one says so
criticScoreSummary: {"score": 99, "max": 100, "reviewCount": 22, ...}userScore: {"score": 9.1} <- no `max` field anywhere
The critic score is 0–100 and declares its maximum. The user score is 0–10 and
declares nothing. Publishing both under a field called "score" invites a
tenfold comparison error, so they are named apart (criticScore /
criticScoreMax, userScore / userScoreMax) and every row carries
scoresUseDifferentScales: true.
3. The sentiment buckets are not a partition of the review count
Measured across 48 games, 7 disagreed — and every one undercounted:
| Title | positive + neutral + negative | reviewCount | shortfall |
|---|---|---|---|
| Zelda: Breath of the Wild | 109 | 117 | 8 |
| Red Dead Redemption 2 | 99 | 109 | 10 |
| Super Mario Odyssey | 114 | 124 | 10 |
| Elden Ring | 86 | 93 | 7 |
Never the other direction, and it clusters on the most-reviewed titles — some
reviews are counted in the total but assigned to no sentiment bucket. Both
figures ship, plus criticSentimentBucketSum, criticReviewsUnbucketed and
criticSentimentBucketsCoverAllReviews, rather than implying the buckets add
up.
4. An unknown productType silently returns games
| Sent | Answer |
|---|---|
productType=nosuchtype | 200 with the games catalogue, 14,335 results |
sortBy=-nosuchfield | HTTP 400 — honest |
genres=nosuchgenre-xyz | 200 with totalResults: 0 — honest |
nosuchparam=x | 200, ignored |
So a typo in the product type answers a different question convincingly. It is
validated locally and refused before a request is spent. sortBy is validated
too — upstream is honest there, but failing early costs nothing.
5. totalResults is true, and the walk ends honestly
Rare enough in this portfolio to state plainly. Games claims 14,335:
offset 14,300 -> 24 rows offset 14,328 -> exactly 7 rows (14,328+7 = 14,335)offset 14,400 -> 0 rows offset 100,000 -> 0 rows
No wrap back to the start, no clamp to the last page, no phantom results.
Bonus: the apiKey is decorative
The same query returns identical data with the correct key, a bogus key, or no key at all. It is embedded in the site's own HTML anyway. This actor never sends one and never caches one — there is nothing to rot.
Output
One SEARCH_SUMMARY per run, one TITLE per title, one ERROR per failure.
TITLE rows carry the upstream object verbatim plus titleSlug, titleUrl,
titleName, metacriticId, productType, itemType, releaseDate,
premiereYear, contentRating, genres, description, criticScore,
criticScoreMax, criticReviewCount, the sentiment breakdown and its
shortfall fields, userScore, userScoreMax, resultOffset and resultRank.
With detail: cast, directors, creators, publishers,
productionCompanies, gamePlatforms, imageUrl, trailerUrl, schemaType.
Limits
- The URL segment differs from the API's
productType—gamevsgames,movievsmovies. A wrong segment is an honest 404. Pass a full metacritic.com URL intitlesmode and the segment is taken from it. - Page size is fixed at 24:
limitabove that is an honest HTTP 400. - Music is not covered — that section serves no JSON-LD and no
__NUXT__payload. - There is no WAF on this host; a proxy is offered but was not needed for any of the reconnaissance.