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

ParameterInRequiredDescription
movie_idpathyesNumeric movie id, for example 550.
api_keyqueryone of twoYour key. Use this or the Authorization: Bearer header.

Response fields

FieldTypeNotes
idintegerMovie id. Not present when you use append_to_response.
imdbobject or nullIMDb score, out of 10 with one decimal.
kinopoiskobject or nullKinopoisk score, out of 10 with one decimal.
metacriticobject or nullMetacritic score, out of 100 as an integer.
rotten_tomatoesobject or nullRotten Tomatoes score, out of 100 as an integer.
tmdbobject or nullTMDB user score, out of 10 with up to three decimals.
valuenumberInside each source object. The score on that source's own scale.
votesinteger or nullInside 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

JSON
{
  "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 pathReturnsParameters
GET /3/movie/{movie_id}/ratingsid and five sourcesmovie_id
GET /3/tv/{tv_id}/ratingsid and five sourcestv_id
curl
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:

JSON
{
  "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.

RequestWhere the scores appear
/3/movie/550?append_to_response=ratingsratings in the movie record
/3/tv/1396?append_to_response=credits,ratingsratings 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.

KeySourceScaleLabel to show
imdbIMDb0 to 10IMDb
kinopoiskKinopoisk0 to 10Kinopoisk
metacriticMetacritic0 to 100Metacritic
rotten_tomatoesRotten Tomatoes0 to 100Rotten Tomatoes
tmdbTMDB0 to 10TMDB

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.

JavaScript
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.

HTTPstatus_codeWhen
4017The key is missing or wrong.
40236The plan period has ended.
40434No movie or series with that id.
42925Over 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=ratings counts 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.