# 电影与电视剧评分

> 接口返回 IMDb、Kinopoisk、Metacritic、Rotten Tomatoes 和 TMDB 评分，每个来源是带 value 和 votes 的对象，未知时为 null。详情调用加 append_to_response=ratings 可合并。

## 接口

`GET /3/movie/{movie_id}/ratings`

```bash
curl "https://api.amodb.dev/3/movie/550/ratings" \
  -H "Authorization: Bearer $AMODB_KEY"
```

响应:

```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": 33042
  }
}
```

## 获取电影的评分

`GET /3/movie/{movie_id}/ratings` 返回各评分来源为一部电影发布的分数。响应包含电影 `id` 和五个来源键：`imdb`、`kinopoisk`、`metacritic`、`rotten_tomatoes` 和 `tmdb`。每个来源键保存一个带有 `value` 和 `votes` 的对象，该来源对此作品未知时为 `null`。

示例加载《搏击俱乐部》（ID 550）并打印每个来源，同时标出未知的来源。

```bash
curl -sf "https://api.amodb.dev/3/movie/550/ratings" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
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');
}
```

```python
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
<?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;
}
```

```go
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)
}
```

### 参数

| 参数         | 位置 | 必填  | 说明                                        |
| ---------- | -- | --- | ----------------------------------------- |
| `movie_id` | 路径 | 是   | 数字形式的电影 ID，例如 `550`。                      |
| `api_key`  | 查询 | 二选一 | 你的密钥。使用它，或使用 `Authorization: Bearer` 请求头。 |

### 响应字段

| 字段                | 类型             | 说明                                  |
| ----------------- | -------------- | ----------------------------------- |
| `id`              | integer        | 电影 ID。使用 `append_to_response` 时不存在。 |
| `imdb`            | object 或 null  | IMDb 评分，满分 10，保留一位小数。               |
| `kinopoisk`       | object 或 null  | Kinopoisk 评分，满分 10，保留一位小数。          |
| `metacritic`      | object 或 null  | Metacritic 评分，满分 100，为整数。           |
| `rotten_tomatoes` | object 或 null  | Rotten Tomatoes 评分，满分 100，为整数。      |
| `tmdb`            | object 或 null  | TMDB 用户评分，满分 10，最多三位小数。             |
| `value`           | number         | 在每个来源对象内。该来源自己刻度上的分数。               |
| `votes`           | integer 或 null | 在每个来源对象内。分数背后的投票数，未知时为 `null`。      |

请按名称读取这五个来源键，并跳过其他任何键。来源键的顺序始终相同：`imdb`、`kinopoisk`、`metacritic`、`rotten_tomatoes`、`tmdb`。

### 示例响应

```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 }
}
```

这里只有 IMDb 和 TMDB 带有投票数。Kinopoisk、Metacritic 和 Rotten Tomatoes 有分数，但投票数为 `null`。

## 获取电视剧的评分

`GET /3/tv/{tv_id}/ratings` 的键和 null 规则相同。传入剧集 ID，例如《绝命毒师》是 `1396`。

| 方法和路径                             | 返回内容       | 参数         |
| --------------------------------- | ---------- | ---------- |
| `GET /3/movie/{movie_id}/ratings` | `id` 和五个来源 | `movie_id` |
| `GET /3/tv/{tv_id}/ratings`       | `id` 和五个来源 | `tv_id`    |

```bash
curl -sf "https://api.amodb.dev/3/tv/1396/ratings" -H "Authorization: Bearer $AMODB_KEY"
```

《绝命毒师》没有 Metacritic 评分，所以该来源为 `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 }
}
```

## 把评分加入详情调用

`append_to_response=ratings` 适用于电影和电视剧详情。该对象出现在 `ratings` 键下，与常规字段并列，自己没有 `id`。列表页每部作品只需一个请求，而不是两个。

| 请求                                              | 评分出现的位置                      |
| ----------------------------------------------- | ---------------------------- |
| `/3/movie/550?append_to_response=ratings`       | 电影记录中的 `ratings`             |
| `/3/tv/1396?append_to_response=credits,ratings` | 剧集记录中的 `ratings` 和 `credits` |

可追加的其他取值见[电影详情](https://amodb.dev/zh/movies/)和[电视剧](https://amodb.dev/zh/tv-series/)。

## 来源与 null 值

来源为 null 不是零分。它表示该作品缺少这个评分。

| 键                 | 来源              | 刻度      | 要显示的标签          |
| ----------------- | --------------- | ------- | --------------- |
| `imdb`            | IMDb            | 0 到 10  | IMDb            |
| `kinopoisk`       | Kinopoisk       | 0 到 10  | Kinopoisk       |
| `metacritic`      | Metacritic      | 0 到 100 | Metacritic      |
| `rotten_tomatoes` | Rotten Tomatoes | 0 到 100 | Rotten Tomatoes |
| `tmdb`            | TMDB            | 0 到 10  | TMDB            |

覆盖范围因作品而异，所以请针对 null 编写代码，而不是依赖固定的来源列表。对任何作品，五个键中的任何一个都可能为 null。

## 评分与 vote_average 的区别

电影或剧集记录上的 `vote_average` 和 `vote_count` 字段是 TMDB 用户评分。`tmdb` 键以与其他四个相同的结构携带同一个分数，所以一次调用就能以统一格式得到全部五个来源。

每个来源都有自己的受众和刻度。不要对它们取平均，也不要拿一个来源的数值去和另一个来源的投票数比较。

## 在应用中显示评分

只渲染存在的来源。卡片上用一个简短的徽章列表就够了。加上刻度，让读者能分清满分 10 的 8.8 和满分 100 的 67。

```js
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}`);
}
```

每个数值都要显示标签。当五个都为 null 时，请隐藏这个区块，而不是显示空徽章。

## 错误

失败时保留 TMDB 的错误响应体，其中 `success` 为 false，`status_code` 指出问题。没有评分的作品不属于其中。

| HTTP | status_code | 何时出现                          |
| ---- | ----------- | ----------------------------- |
| 401  | 7           | 密钥缺失或错误。                      |
| 402  | 36          | 套餐周期已结束。                      |
| 404  | 34          | 没有该 ID 的电影或剧集。                |
| 429  | 25          | 超过每分钟请求数。请等待 `Retry-After` 秒。 |

未知的评分是 `null`，不是错误。详情见[错误](https://amodb.dev/zh/errors/)和[速率限制](https://amodb.dev/zh/rate-limits/)。

## 实用说明

- 评分和详情一样在边缘缓存 6 小时。更频繁地刷新只会得到相同的响应体。
- 带有 `append_to_response=ratings` 的请求只占你每分钟 20、40 或 120 个请求中的一个。
- 保存评分时同时记录获取日期。投票数会随时间增长，数值也会变化。
- 想先把 IMDb 或 Kinopoisk ID 映射到作品，请用[按外部 ID 查找](https://amodb.dev/zh/find-by-external-id/)。想了解感受而不是分数，见[情绪指数](https://amodb.dev/zh/moods/)。

## 常见问题

### 为什么某个来源是 null？

null 表示该作品的这个评分缺失或为零。新上映的作品或地区性影片，来源往往比知名作品少，所以格式化数字之前请务必先处理 null。votes 字段也可能是 null。

### 这与 vote_average 有什么区别？

详情记录上的 vote_average 字段只是 TMDB 用户评分。评分接口把它放在 tmdb 键下再返回一次，保留三位小数，并加上另外四个来源，每个都带 value 和 votes。

### 可以把五个评分取平均吗？

不要这样做。IMDb、Kinopoisk 和 TMDB 满分 10，而 Metacritic 和 Rotten Tomatoes 满分 100。每个来源的受众也不同，而且有些为 null。请把你拥有的来源连同标签并排展示。

### 调用评分接口会额外消耗请求吗？

它和其他调用一样算一次请求。使用 append_to_response=ratings 时，评分包含在详情调用中，所以你每分钟只占用一个请求名额，而不是两个。

## 相关页面

- [电影详情 API](https://amodb.dev/zh/movies/): 路径和 JSON 与 TMDB v3 相同的电影详情 API：按 ID 取电影，用 append_to_response 加入演职员和评分，支持 14 种语言。
- [电视剧 API](https://amodb.dev/zh/tv-series/): 路径和 JSON 与 TMDB v3 相同的电视剧 API。按 ID 获取剧集的季、电视网和下一集，以及内容分级和观看平台。
- [按外部 ID 查找](https://amodb.dev/zh/find-by-external-id/): 用 TMDB find 接口按 IMDb、TVDB、Wikidata 或 Kinopoisk ID 查找电影、剧集或人物，并读取外部 ID。
- [情绪指数](https://amodb.dev/zh/moods/): 电影情绪 API，每部作品提供 0 到 10 的情绪指数。读取电影或剧集的情绪，用 append_to_response=moods 合并，构建徽章或筛选。
- [速率限制](https://amodb.dev/zh/rate-limits/): amodb 按滑动 60 秒窗口内每分钟请求数限制每个密钥：按套餐为 20、40 或 120。读取速率限制响应头，并用 Retry-After 处理 429。
- [快速开始](https://amodb.dev/zh/quickstart/): 发出第一次 amodb 调用：获取密钥，用 curl、JavaScript、Python、PHP 或 Go 请求电影详情，再加载海报。

更新于: 2026-10-18. Source: https://amodb.dev/zh/ratings/
