Movie and TV ratings
GET /3/movie/{movie_id}/ratings and GET /3/tv/{tv_id}/ratings return IMDb, Kinopoisk, Metacritic, Rotten Tomatoes and TMDB scores. Each source is an object with value and votes, or null when unknown. IMDb, Kinopoisk and TMDB are out of 10, the other two out of 100. Add append_to_response=ratings to merge it into details.
Get ratings for a movie
GET /3/movie/{movie_id}/ratings returns the scores that rating sources publish for one movie. The reply has the movie id and five source keys: imdb, kinopoisk, metacritic, rotten_tomatoes and tmdb. Each source key holds an object with value and votes, or null when that source is not known for the title.
The sample loads Fight Club (id 550) and prints every source, marking the unknown ones.
curl -sf "https://api.amodb.dev/3/movie/550/ratings" \
-H "Authorization: Bearer $AMODB_KEY"const res = await fetch('https://api.amodb.dev/3/movie/550/ratings', {
headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` },
});
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const ratings = await res.json();
for (const source of ['imdb', 'kinopoisk', 'metacritic', 'rotten_tomatoes', 'tmdb']) {
const score = ratings[source];
console.log(source, score ? `${score.value} (votes: ${score.votes ?? 'unknown'})` : 'not known');
}import os
import requests
r = requests.get(
"https://api.amodb.dev/3/movie/550/ratings",
headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
timeout=10,
)
r.raise_for_status()
ratings = r.json()
for source in ("imdb", "kinopoisk", "metacritic", "rotten_tomatoes", "tmdb"):
score = ratings[source]
if score is None:
print(source, "not known")
else:
print(source, score["value"], "votes:", score["votes"] or "unknown")<?php
$ch = curl_init('https://api.amodb.dev/3/movie/550/ratings');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("amodb returned $status");
}
$ratings = json_decode($body, true);
foreach (['imdb', 'kinopoisk', 'metacritic', 'rotten_tomatoes', 'tmdb'] as $source) {
$score = $ratings[$source];
echo $source, ' ', $score === null ? 'not known' : $score['value'] . ' votes: ' . ($score['votes'] ?? 'unknown'), PHP_EOL;
}package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Score struct {
Value float64 `json:"value"`
Votes *int `json:"votes"`
}
type Ratings struct {
IMDb *Score `json:"imdb"`
Kinopoisk *Score `json:"kinopoisk"`
Metacritic *Score `json:"metacritic"`
RottenTomatoes *Score `json:"rotten_tomatoes"`
TMDB *Score `json:"tmdb"`
}
func show(name string, s *Score) {
if s == nil {
fmt.Println(name, "not known")
return
}
if s.Votes == nil {
fmt.Println(name, s.Value, "votes: unknown")
return
}
fmt.Println(name, s.Value, "votes:", *s.Votes)
}
func main() {
req, _ := http.NewRequest("GET", "https://api.amodb.dev/3/movie/550/ratings", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
client := &http.Client{Timeout: 10 * time.Second}
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
panic(res.Status)
}
var r Ratings
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
panic(err)
}
show("imdb", r.IMDb)
show("kinopoisk", r.Kinopoisk)
show("metacritic", r.Metacritic)
show("rotten_tomatoes", r.RottenTomatoes)
show("tmdb", r.TMDB)
}Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
movie_id | path | yes | Numeric movie id, for example 550. |
api_key | query | one of two | Your key. Use this or the Authorization: Bearer header. |
Response fields
| Field | Type | Notes |
|---|---|---|
id | integer | Movie id. Not present when you use append_to_response. |
imdb | object or null | IMDb score, out of 10 with one decimal. |
kinopoisk | object or null | Kinopoisk score, out of 10 with one decimal. |
metacritic | object or null | Metacritic score, out of 100 as an integer. |
rotten_tomatoes | object or null | Rotten Tomatoes score, out of 100 as an integer. |
tmdb | object or null | TMDB user score, out of 10 with up to three decimals. |
value | number | Inside each source object. The score on that source's own scale. |
votes | integer or null | Inside each source object. Number of votes behind the score, or null when it is not known. |
Read the five source keys by name and skip any other key. The source keys always come in the same order: imdb, kinopoisk, metacritic, rotten_tomatoes, tmdb.
Example response
{
"id": 550,
"imdb": { "value": 8.8, "votes": 2669989 },
"kinopoisk": { "value": 8.7, "votes": null },
"metacritic": { "value": 67, "votes": null },
"rotten_tomatoes": { "value": 81, "votes": null },
"tmdb": { "value": 8.437, "votes": 33039 }
}Only IMDb and TMDB come with a vote count here. Kinopoisk, Metacritic and Rotten Tomatoes have a score and a null vote count.
Get ratings for a TV series
GET /3/tv/{tv_id}/ratings has the same keys and the same null rules. Pass the series id, for example 1396 for Breaking Bad.
| Method and path | Returns | Parameters |
|---|---|---|
GET /3/movie/{movie_id}/ratings | id and five sources | movie_id |
GET /3/tv/{tv_id}/ratings | id and five sources | tv_id |
curl -sf "https://api.amodb.dev/3/tv/1396/ratings" -H "Authorization: Bearer $AMODB_KEY"Breaking Bad has no Metacritic score, so that source is null:
{
"id": 1396,
"imdb": { "value": 9.5, "votes": 2671962 },
"kinopoisk": { "value": 8.9, "votes": null },
"metacritic": null,
"rotten_tomatoes": { "value": 96, "votes": null },
"tmdb": { "value": 9, "votes": 18863 }
}Add ratings to a details call
append_to_response=ratings works on movie and TV details. The object arrives under the ratings key next to the usual fields, without its own id. A list page needs one request per title instead of two.
| Request | Where the scores appear |
|---|---|
/3/movie/550?append_to_response=ratings | ratings in the movie record |
/3/tv/1396?append_to_response=credits,ratings | ratings and credits in the series record |
See movie details and TV series for the other values you can append.
Sources and null values
A null source is not a zero score. It means the score is missing for the title.
| Key | Source | Scale | Label to show |
|---|---|---|---|
imdb | IMDb | 0 to 10 | IMDb |
kinopoisk | Kinopoisk | 0 to 10 | Kinopoisk |
metacritic | Metacritic | 0 to 100 | Metacritic |
rotten_tomatoes | Rotten Tomatoes | 0 to 100 | Rotten Tomatoes |
tmdb | TMDB | 0 to 10 | TMDB |
Coverage varies by title, so code against null, not against a fixed list of sources. Any of the five keys can be null for any title.
How ratings differ from vote_average
The vote_average and vote_count fields on a movie or series record are the TMDB user score. The tmdb key carries the same score in the same shape as the other four, so one call gives you all five sources in one format.
Each source has its own audience and scale. Do not average them and do not compare a value from one source with the vote count of another.
Show ratings in an app
Render only the sources that are present. A short badge list is enough for a card. Add the scale so a reader can tell 8.8 out of 10 from 67 out of 100.
const labels = {
imdb: ['IMDb', 10],
kinopoisk: ['Kinopoisk', 10],
metacritic: ['Metacritic', 100],
rotten_tomatoes: ['Rotten Tomatoes', 100],
tmdb: ['TMDB', 10],
};
function badges(ratings) {
return Object.entries(labels)
.filter(([key]) => ratings[key])
.map(([key, [label, max]]) => `${label} ${ratings[key].value}/${max}`);
}Show the label with every value. When all five are null, hide the block instead of showing empty badges.
Errors
Failures keep the TMDB error body, where success is false and status_code names the problem. A title with no scores is not one of them.
| HTTP | status_code | When |
|---|---|---|
| 401 | 7 | The key is missing or wrong. |
| 402 | 36 | The plan period has ended. |
| 404 | 34 | No movie or series with that id. |
| 429 | 25 | Over your requests per minute. Wait for the Retry-After seconds. |
An unknown score is null, not an error. Details are on errors and rate limits.
Practical notes
- Ratings are cached at the edge for 6 hours, like details. Refreshing more often returns the same body.
- A request with
append_to_response=ratingscounts as one request against your 20, 40 or 120 requests per minute. - Store scores with the date you fetched them. Votes grow over time and the numbers move.
- To map an IMDb or Kinopoisk id to a title first, use find by external id. For feelings rather than scores, see moods.