# 用于自建片库的电影数据库 API

> 把 amodb 作为自建电影数据库的数据源。电影、人物、公司、系列合集、关键词和类型共用数字 ID，行与行可直接关联。用 discover 按上映日期窗口分页灌库，按时效刷新，再通过外部 ID 查找匹配自己的 ID。

## 用 API 构建你自己的电影数据库

amodb 是独立的 API，使用 TMDB v3 协议。电影、人物、公司、系列合集、关键词和类型都有数字 ID 并相互指向，因此你可以用它填充 SQL 表并保持更新。用 discover 分页灌库，按时效刷新，再用 `find` 匹配你自己的标识符。amodb 没有变更动态，所以刷新就是对保留的行重新获取。

你会得到：

- 六种相互关联、ID 稳定的实体类型。
- 用于首次导入的 discover 分页：每页最多 20 条结果，每次查询 500 页。
- discover 上的上映日期窗口，用来切分首次导入并列出新上映的影片。
- 针对 `imdb_id`、`tvdb_id`、`wikidata_id` 和 `kinopoisk_id` 的外部 ID 查找。

## 可获取的数据

这是数据模型。每一行对应关系型副本中的一张表。

| 实体   | 读取方式                                | 键              | 关联到                                                                              |
| ---- | ----------------------------------- | -------------- | -------------------------------------------------------------------------------- |
| 电影   | `GET /3/movie/{movie_id}`           | `id`、`imdb_id` | `genres[].id`、`production_companies[].id`、`belongs_to_collection.id`             |
| 人物   | `GET /3/person/{person_id}`         | `id`、`imdb_id` | 通过 `/3/movie/{movie_id}/credits` 关联电影                                            |
| 公司   | `GET /3/company/{company_id}`       | `id`           | 通过 discover 的 `with_companies` 关联电影。`parent_company` 始终为 `null`，`description` 为空 |
| 系列合集 | `GET /3/collection/{collection_id}` | `id`           | `parts[]`，即电影列表                                                                  |
| 关键词  | `GET /3/keyword/{keyword_id}`       | `id`           | 通过 `/3/keyword/{keyword_id}/movies` 或 `with_keywords` 关联电影                       |
| 类型   | `GET /3/genre/movie/list`           | `id`           | 电影上的 `genres[].id`                                                               |

多对多关系需要关联表：`movie_genre`、`movie_company`、`movie_keyword` 和 `movie_credit`。最后一张表请用[演职员](https://amodb.dev/zh/credits/)中的 `credit_id` 作为键。字段列表见[电影](https://amodb.dev/zh/movies/)、[人物](https://amodb.dev/zh/people/)和[系列合集](https://amodb.dev/zh/collections/)。

## 三步开始

1. 在[定价](https://amodb.dev/zh/pricing/)页比较三种请求速率。速率越高，首次导入越短，其他都不变。
2. 在账户后台创建密钥，并保存在环境变量中。
3. 用一天的上映日期窗口调用 `/3/discover/movie`，再获取列表中的每个 ID。

## 请求示例

这个 PHP 脚本通过 discover 列出昨天和今天上映的电影，并把每条记录写入（upsert）SQLite 表。它在两次详情读取之间休眠 3 秒，以保持在每分钟 20 个请求以内；遇到 429 时按 `Retry-After` 重试；并删除 ID 现在返回 404 的行。

```bash
curl -s "https://api.amodb.dev/3/discover/movie?primary_release_date.gte=2026-10-17&primary_release_date.lte=2026-10-18&sort_by=primary_release_date.asc&page=1" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```php
<?php
const BASE = 'https://api.amodb.dev/3';

function amodb(string $path, array $query = []): ?array
{
    $url = BASE . $path . '?' . http_build_query($query);
    for ($attempt = 0; $attempt < 3; $attempt++) {
        $retryAfter = 1;
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
            CURLOPT_HEADERFUNCTION => function ($handle, $line) use (&$retryAfter) {
                if (stripos($line, 'Retry-After:') === 0) {
                    $retryAfter = max(1, (int) trim(substr($line, 12)));
                }
                return strlen($line);
            },
        ]);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($status === 429) {
            sleep($retryAfter);
            continue;
        }
        if ($status === 404) {
            return null;
        }
        if ($status !== 200) {
            throw new RuntimeException("amodb returned $status for $path");
        }
        return json_decode($body, true);
    }
    throw new RuntimeException("Still rate limited on $path");
}

$db = new PDO('sqlite:movies.sqlite');
$db->exec('CREATE TABLE IF NOT EXISTS movie (id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, release_date TEXT, raw TEXT)');
$save = $db->prepare('INSERT OR REPLACE INTO movie (id, imdb_id, title, release_date, raw) VALUES (?, ?, ?, ?, ?)');
$drop = $db->prepare('DELETE FROM movie WHERE id = ?');

$from = date('Y-m-d', strtotime('-1 day'));
$to = date('Y-m-d');
$page = 1;
do {
    $list = amodb('/discover/movie', [
        'primary_release_date.gte' => $from,
        'primary_release_date.lte' => $to,
        'sort_by' => 'primary_release_date.asc',
        'page' => $page,
    ]);
    foreach ($list['results'] as $row) {
        $movie = amodb('/movie/' . $row['id'], ['language' => 'en-US']);
        if ($movie === null) {
            $drop->execute([$row['id']]);
            continue;
        }
        $save->execute([$movie['id'], $movie['imdb_id'] ?? null, $movie['title'], $movie['release_date'] ?? null, json_encode($movie)]);
        sleep(3);
    }
    $page++;
} while ($page <= min($list['total_pages'], 500));
```

discover 返回的是列表行，而不是完整记录。这是第 1 页 20 条结果中的 2 条。其余字段请读取 `/3/movie/{id}`。

```json
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/siepLRGVjM8yLKppApkD3uSh8Y0.jpg",
      "genre_ids": [
        35
      ],
      "id": 1800227,
      "original_language": "en",
      "original_title": "Sly's Pack",
      "overview": "Nesmith, Gary, & Tony are smoking weed on the roof of a parking garage, but a...",
      "popularity": 0.0071,
      "poster_path": "/3HpIWZUI7jmpKZkfZNdchS2WsvN.jpg",
      "release_date": "2026-10-17",
      "title": "Sly's Pack",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    },
    {
      "adult": false,
      "backdrop_path": null,
      "genre_ids": [
        99
      ],
      "id": 1800168,
      "original_language": "en",
      "original_title": "The Good Life",
      "overview": "The Good LIfe is based on the best-selling book by Drs. Robert Waldinger and...",
      "popularity": 0.0143,
      "poster_path": null,
      "release_date": "2026-10-17",
      "title": "The Good Life",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    }
  ],
  "total_pages": 15,
  "total_results": 299
}
```

## 填充并刷新数据表

| 任务        | 调用                                                                                      | 限制                                                                                            |
| --------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| 首次导入      | 带 `sort_by` 和日期窗口的 `GET /3/discover/movie`                                              | `page` 取 1 到 500，每页最多 20 条结果，所以每次查询 10,000 条。要获取更多，请按 `primary_release_date.gte` 和 `.lte` 切分。 |
| 新上映       | `primary_release_date.gte` 设为上次运行时间的 `GET /3/discover/movie`，以及 `GET /3/movie/upcoming` | 缓存 30 分钟。                                                                                     |
| 刷新行       | 对超过 6 小时的行调用 `GET /3/movie/{movie_id}`                                                  | 没有变更动态。请优先刷新用户打开的作品，其余的刷新频率低一些。                                                               |
| 关联你自己的 ID | `GET /3/find/{external_id}`                                                             | 每次调用一个 ID，来源由 `external_source` 指定。                                                           |
| 按需填充      | 用户打开作品时调用 `GET /3/movie/{movie_id}`                                                     | 把响应连同时间戳一起保存。                                                                                 |

分页窗口是主要的设计选择。当响应中的 `total_pages` 超过 500 时，说明窗口内的行数超过 10,000，请不断对半切分直到满足。近期的一年需要按月切分，一天的上映就有几百部作品。请保存已完成的切片，这样中断的运行可以继续。关联的匹配规则见[按外部 ID 查找](https://amodb.dev/zh/find-by-external-id/)。

## 估算同步规模

每刷新一行就消耗一次详情读取。每分钟 20 个请求的套餐每小时可调用 1,200 次，40 个的是 2,400 次，120 个的是 7,200 次。一个 discover 页面用一次调用列出 20 个 ID。如果你的应用共用这个密钥，请在非高峰时段运行刷新，因为限制是按密钥而不是按脚本计算的。报告窗口内剩余额度的响应头见[速率限制](https://amodb.dev/zh/rate-limits/)。

## 两层缓存

| 层     | 内容                 | TTL                      |
| ----- | ------------------ | ------------------------ |
| 边缘缓存  | 详情和演职员             | 6 小时                     |
| 边缘缓存  | discover、热门趋势和热门列表 | 30 分钟                    |
| 边缘缓存  | 搜索                 | 10 分钟                    |
| 边缘缓存  | 配置                 | 24 小时                    |
| 边缘缓存  | 图片                 | 30 天                     |
| 你的数据表 | 你保留的行              | 直到你的时效规则要求重新获取，且不短于 6 小时 |

边缘缓存在各密钥之间共享，但速率限制先于它执行，所以缓存命中仍会占用你这一分钟中的一个请求。每 6 小时内重复读取同一行只会得到相同的副本并浪费额度。请让时效规则来决定何时重新获取。

## 对比

| 能力       | 自己抓取页面      | 静态数据集文件    | amodb                       |
| -------- | ----------- | ---------- | --------------------------- |
| 新鲜度      | 取决于你上次爬取的时间 | 取决于最近发布的文件 | 详情缓存 6 小时，按时效重新获取           |
| 增量更新     | 重新爬取并比对     | 下载新文件      | 带上映日期窗口的 discover，没有变更动态    |
| 跨实体的 ID  | 页面 URL      | 由发布者定义     | 数字 ID、`imdb_id` 和 `/3/find` |
| 你需要运行的东西 | 爬虫和存储       | 存储和导入任务    | 存储和同步脚本                     |
| 速率限制     | 由你爬取的网站决定   | 不适用        | 每分钟 20、40 或 120 个请求，按密钥计算   |
| 每条记录的语言  | 各页面提供什么就是什么 | 取决于文件      | 14 种，用 `language` 选择        |

## 定价

首次导入是成本最高的部分，它受每分钟请求数限制。请在[定价](https://amodb.dev/zh/pricing/)页选择每分钟 20、40 或 120 个请求。可预付 1、3、6、12 或 24 个月，分别享受 0、10、20、30 或 40% 的折扣。不会自动续费，没有免费额度，所有套餐读取的数据相同。

## 常见问题

### 如何让本地副本保持新鲜？

amodb 没有变更动态，所以按时效刷新。详情在边缘缓存 6 小时，因此同一行重新获取的间隔不要短于这个时间。用 discover 并把 primary_release_date.gte 设为上次运行的时间，即可获取新上映的影片。

### 如何导入第一份副本？

遍历 /3/discover/movie，page 取 1 到 500（每页最多 20 条，所以每次查询 10,000 条），再按 primary_release_date 窗口切分以获取更多。仅 2025 年 3 月就有约 4,400 部作品。然后对保留的每个 ID 读取 /3/movie/{id}。

### 按我的速率限制，首次导入要多久？

每分钟 20 个请求时每小时可发 1,200 次调用，40 个时是 2,400 次，120 个时是 7,200 次。用你需要的详情读取次数除以这个数字，就能规划运行时间。

### 能把 amodb 的 ID 与我已有的 ID 关联吗？

可以。/3/find/{external_id} 接受 imdb_id、tvdb_id、wikidata_id 和 kinopoisk_id，并返回匹配的 amodb 记录。电影记录也带有 imdb_id，因此关联在两个方向上都成立。

### 搭建数据库有免费额度吗？

没有。访问需预付 1、3、6、12 或 24 个月，不会自动续费。每个套餐读取的数据相同，更大的套餐只会让首次导入和刷新跑得更快。

## 相关页面

- [电影详情 API](https://amodb.dev/zh/movies/): 路径和 JSON 与 TMDB v3 相同的电影详情 API：按 ID 取电影，用 append_to_response 加入演职员和评分，支持 14 种语言。
- [人物详情 API](https://amodb.dev/zh/people/): 路径和 JSON 与 TMDB v3 相同的人物详情 API。按 ID 获取演员或幕后人员，含传记、作品列表、外部 ID 和热门列表。
- [系列合集 API](https://amodb.dev/zh/collections/): 采用 TMDB v3 路径的系列合集 API：按 ID 获取系列及全部成员影片，读取电影的 belongs_to_collection，并按名称搜索合集。
- [公司与电视网](https://amodb.dev/zh/companies-networks/): 按 TMDB v3 路径查询制作公司和电视网：按 ID 读取名称、国家和标志，再按公司或电视网 ID 筛选 discover。
- [按外部 ID 查找](https://amodb.dev/zh/find-by-external-id/): 用 TMDB find 接口按 IMDb、TVDB、Wikidata 或 Kinopoisk ID 查找电影、剧集或人物，并读取外部 ID。
- [速率限制](https://amodb.dev/zh/rate-limits/): amodb 按滑动 60 秒窗口内每分钟请求数限制每个密钥：按套餐为 20、40 或 120。读取速率限制响应头，并用 Retry-After 处理 429。

更新于: 2026-10-18. Source: https://amodb.dev/zh/movie-database-api/
