# 电视剧季与单集 API

> 用 GET /3/tv/{series_id}/season/{season_number} 读取一季及其全部单集，再深一层读取单集。JSON 与 TMDB v3 一致，每集带 crew 和 guest_stars，第 0 季存放特别篇。

## 接口

`GET /3/tv/{series_id}/season/{season_number}`

```bash
curl "https://api.amodb.dev/3/tv/1396/season/1?language=en-US" \
  -H "Authorization: Bearer $AMODB_KEY"
```

响应:

```json
{
  "air_date": "2008-01-20",
  "episodes": [
    {
      "air_date": "2008-01-20",
      "episode_number": 1,
      "episode_type": "standard",
      "id": 62085,
      "name": "Pilot",
      "overview": "When an unassuming high school chemistry teacher discovers he has a rare form of lung cancer, he decides to team up with a former student and create a top of the line crystal meth in a used RV, to provide for his family once he is gone.",
      "production_code": "",
      "runtime": 59,
      "season_number": 1,
      "show_id": 1396,
      "still_path": "/ydlY3iPfeOAvu8gVqrxPoMvzNCn.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "crew": [
        {
          "department": "Camera",
          "job": "Director of Photography",
          "credit_id": "52b7029219c29533d00dd2c1",
          "adult": false,
          "gender": 2,
          "id": 2483,
          "known_for_department": "Camera",
          "name": "约翰·托尔",
          "original_name": "约翰·托尔",
          "popularity": 1.1957,
          "profile_path": "/cfL9A6tC7L5Ps5fq1o3WpVKGMb1.jpg"
        },
        {
          "department": "Directing",
          "job": "Director",
          "credit_id": "52542275760ee313280006e8",
          "adult": false,
          "gender": 2,
          "id": 66633,
          "known_for_department": "Writing",
          "name": "文斯·吉利根",
          "original_name": "文斯·吉利根",
          "popularity": 2.7786,
          "profile_path": "/wJIxATESjzsjSgvRhVavOmdamXc.jpg"
        },
        {
          "department": "Writing",
          "job": "Writer",
          "credit_id": "52542275760ee313280006ce",
          "adult": false,
          "gender": 2,
          "id": 66633,
          "known_for_department": "Writing",
          "name": "文斯·吉利根",
          "original_name": "文斯·吉利根",
          "popularity": 2.7786,
          "profile_path": "/wJIxATESjzsjSgvRhVavOmdamXc.jpg"
        }
      ],
      "guest_stars": [
        {
          "character": "Steven Gomez",
          "credit_id": "5271b489760ee35b3e0881a7",
          "order": 8,
          "adult": false,
          "gender": 2,
          "id": 61535,
          "known_for_department": "Acting",
          "name": "史蒂文·迈克尔·奎扎达",
          "original_name": "史蒂文·迈克尔·奎扎达",
          "popularity": 1.7511,
          "profile_path": "/pVYrDkwI6GWvCNL2kJhpDJfBFyd.jpg"
        },
        {
          "character": "Jock",
          "credit_id": "52542275760ee313280006b4",
          "order": 500,
          "adult": false,
          "gender": 2,
          "id": 1216132,
          "known_for_department": "Acting",
          "name": "Aaron Hill",
          "original_name": "Aaron Hill",
          "popularity": 1.6521,
          "profile_path": "/rNp31SeoVqSQU6OZWxZUhGwAgyq.jpg"
        },
        {
          "character": "Dr. Belknap",
          "credit_id": "52725cb1760ee3044d0b9984",
          "order": 502,
          "adult": false,
          "gender": 2,
          "id": 161591,
          "known_for_department": "Acting",
          "name": "Gregory Chase",
          "original_name": "Gregory Chase",
          "popularity": 0.5435,
          "profile_path": "/gNdodev00CROpXuAh5EFmkWTVOo.jpg"
        }
      ]
    },
    {
      "air_date": "2008-01-27",
      "episode_number": 2,
      "episode_type": "standard",
      "id": 62086,
      "name": "Cat's in the Bag...",
      "overview": "Walt and Jesse attempt to tie up loose ends. The desperate situation gets more complicated with the flip of a coin. Walt's wife, Skyler, becomes suspicious of Walt's strange behavior.",
      "production_code": "",
      "runtime": 49,
      "season_number": 1,
      "show_id": 1396,
      "still_path": "/AbMoecO0ZZio0LcgeLxlzdyGs6X.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "crew": [
        {
          "department": "Camera",
          "job": "Director of Photography",
          "credit_id": "52b7049c19c2953b63015013",
          "adult": false,
          "gender": 2,
          "id": 17948,
          "known_for_department": "Camera",
          "name": "Reynaldo Villalobos",
          "original_name": "Reynaldo Villalobos",
          "popularity": 0.904,
          "profile_path": "/4YlfhHDRZWhbRgeqT2Bwl1u5p9j.jpg"
        },
        {
          "department": "Writing",
          "job": "Writer",
          "credit_id": "52542275760ee313280006ce",
          "adult": false,
          "gender": 2,
          "id": 66633,
          "known_for_department": "Writing",
          "name": "文斯·吉利根",
          "original_name": "文斯·吉利根",
          "popularity": 2.7786,
          "profile_path": "/wJIxATESjzsjSgvRhVavOmdamXc.jpg"
        },
        {
          "department": "Directing",
          "job": "Director",
          "credit_id": "52542275760ee31328000725",
          "adult": false,
          "gender": 2,
          "id": 111338,
          "known_for_department": "Directing",
          "name": "亚当·伯恩斯坦",
          "original_name": "亚当·伯恩斯坦",
          "popularity": 1.0511,
          "profile_path": "/mnob89jXPcPfW85PPw7tTPCKSig.jpg"
        }
      ],
      "guest_stars": [
        {
          "character": "Krazy-8",
          "credit_id": "52725845760ee3046b09426e",
          "order": 504,
          "adult": false,
          "gender": 2,
          "id": 1046460,
          "known_for_department": "Acting",
          "name": "Max Arciniega",
          "original_name": "Max Arciniega",
          "popularity": 1.4399,
          "profile_path": "/eqKAJKPpt41KpsLIkkBnAY4HMAL.jpg"
        },
        {
          "character": "Ob-Gyn",
          "credit_id": "63012c9c5f4b73008267ba53",
          "order": 682,
          "adult": false,
          "gender": 0,
          "id": 1448105,
          "known_for_department": "Acting",
          "name": "Shane Marinson",
          "original_name": "Shane Marinson",
          "popularity": 0,
          "profile_path": null
        },
        {
          "character": "Ben",
          "credit_id": "63012d2111c066007cbc37b1",
          "order": 683,
          "adult": false,
          "gender": 0,
          "id": 3670897,
          "known_for_department": "Acting",
          "name": "Jason Byrd",
          "original_name": "Jason Byrd",
          "popularity": 0,
          "profile_path": null
        }
      ]
    },
    {
      "air_date": "2008-02-10",
      "episode_number": 3,
      "episode_type": "standard",
      "id": 62087,
      "name": "...And the Bag's in the River",
      "overview": "Walter fights with Jesse over his drug use, causing him to leave Walter alone with their captive, Krazy-8. Meanwhile, Hank has a scared straight moment with Walter Jr. after his aunt discovers he has been smoking pot. Also, Skylar is upset...",
      "production_code": "",
      "runtime": 49,
      "season_number": 1,
      "show_id": 1396,
      "still_path": "/2kBeBlxGqBOdWlKwzAxiwkfU5on.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "crew": [
        {
          "department": "Camera",
          "job": "Director of Photography",
          "credit_id": "52b7049c19c2953b63015013",
          "adult": false,
          "gender": 2,
          "id": 17948,
          "known_for_department": "Camera",
          "name": "Reynaldo Villalobos",
          "original_name": "Reynaldo Villalobos",
          "popularity": 0.904,
          "profile_path": "/4YlfhHDRZWhbRgeqT2Bwl1u5p9j.jpg"
        },
        {
          "department": "Writing",
          "job": "Writer",
          "credit_id": "52542275760ee313280006ce",
          "adult": false,
          "gender": 2,
          "id": 66633,
          "known_for_department": "Writing",
          "name": "文斯·吉利根",
          "original_name": "文斯·吉利根",
          "popularity": 2.7786,
          "profile_path": "/wJIxATESjzsjSgvRhVavOmdamXc.jpg"
        },
        {
          "department": "Directing",
          "job": "Director",
          "credit_id": "52542275760ee31328000725",
          "adult": false,
          "gender": 2,
          "id": 111338,
          "known_for_department": "Directing",
          "name": "亚当·伯恩斯坦",
          "original_name": "亚当·伯恩斯坦",
          "popularity": 1.0511,
          "profile_path": "/mnob89jXPcPfW85PPw7tTPCKSig.jpg"
        }
      ],
      "guest_stars": [
        {
          "character": "Steven Gomez",
          "credit_id": "5271b489760ee35b3e0881a7",
          "order": 8,
          "adult": false,
          "gender": 2,
          "id": 61535,
          "known_for_department": "Acting",
          "name": "史蒂文·迈克尔·奎扎达",
          "original_name": "史蒂文·迈克尔·奎扎达",
          "popularity": 1.7511,
          "profile_path": "/pVYrDkwI6GWvCNL2kJhpDJfBFyd.jpg"
        },
        {
          "character": "Krazy-8",
          "credit_id": "52725845760ee3046b09426e",
          "order": 504,
          "adult": false,
          "gender": 2,
          "id": 1046460,
          "known_for_department": "Acting",
          "name": "Max Arciniega",
          "original_name": "Max Arciniega",
          "popularity": 1.4399,
          "profile_path": "/eqKAJKPpt41KpsLIkkBnAY4HMAL.jpg"
        },
        {
          "character": "Carmen Molina",
          "credit_id": "52542273760ee31328000676",
          "order": 643,
          "adult": false,
          "gender": 1,
          "id": 115688,
          "known_for_department": "Acting",
          "name": "卡门·塞兰诺",
          "original_name": "卡门·塞兰诺",
          "popularity": 1.1342,
          "profile_path": "/nzJEe2UqvvMIBJZP1aeFEj4EunN.jpg"
        }
      ]
    }
  ],
  "name": "Season 1",
  "overview": "High school chemistry teacher Walter White's life is suddenly transformed by a dire medical diagnosis. Street-savvy former student Jesse Pinkman \"teaches\" Walter a new trade.",
  "id": 3572,
  "poster_path": "/91EIXdWxmp2mHNRsSuDW3WyDJLj.jpg",
  "season_number": 1,
  "vote_average": 0
}
```

## 获取一季

`GET /3/tv/{series_id}/season/{season_number}` 在一个响应中返回一季及其完整的 `episodes` 数组。季号从 `1` 开始，`0` 保留给特别篇。剧集 ID 和季号取自[电视剧](https://amodb.dev/zh/tv-series/)记录。

示例读取《绝命毒师》第 1 季。代码为每一集打印一行。

```bash
curl -sSf "https://api.amodb.dev/3/tv/1396/season/1?language=en-US" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const url = 'https://api.amodb.dev/3/tv/1396/season/1?language=en-US';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const season = await res.json();
for (const ep of season.episodes) {
  console.log(String(ep.episode_number).padStart(2, '0'), ep.name, ep.air_date, `${ep.runtime ?? '?'} min`);
}
```

```python
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/tv/1396/season/1",
    params={"language": "en-US"},
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
season = r.json()
for ep in season["episodes"]:
    print(f"{ep['episode_number']:02d}", ep["name"], ep["air_date"], ep["runtime"] or "?")
```

```php
<?php
$url = 'https://api.amodb.dev/3/tv/1396/season/1?language=en-US';
$ch = curl_init($url);
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");
}
$season = json_decode($body, true);
foreach ($season['episodes'] as $ep) {
    printf("%02d %s %s\n", $ep['episode_number'], $ep['name'], $ep['air_date']);
}
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type Season struct {
	Name     string `json:"name"`
	Episodes []struct {
		EpisodeNumber int    `json:"episode_number"`
		Name          string `json:"name"`
		AirDate       string `json:"air_date"`
		Runtime       int    `json:"runtime"`
	} `json:"episodes"`
}

func main() {
	req, _ := http.NewRequest("GET", "https://api.amodb.dev/3/tv/1396/season/1?language=en-US", 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 s Season
	if err := json.NewDecoder(res.Body).Decode(&s); err != nil {
		panic(err)
	}
	for _, e := range s.Episodes {
		fmt.Printf("%02d %s %s %d min\n", e.EpisodeNumber, e.Name, e.AirDate, e.Runtime)
	}
}
```

### 参数

| 参数                   | 位置 | 必填  | 说明                                                          |
| -------------------- | -- | --- | ----------------------------------------------------------- |
| `series_id`          | 路径 | 是   | 数字形式的电视剧 ID，例如 `1396`。                                      |
| `season_number`      | 路径 | 是   | 整数季号。`0` 返回特别篇。                                             |
| `api_key`            | 查询 | 二选一 | 你的密钥。使用它，或使用 `Authorization: Bearer` 请求头。                   |
| `language`           | 查询 | 否   | `en-US` 或 `fr-FR` 这样的 TMDB 标签。提供 14 种基础语言。默认 `en-US`。       |
| `append_to_response` | 查询 | 否   | 季一级的取值：`credits`、`external_ids`、`images`、`videos`。其他名称会被忽略。 |

### 响应字段

| 字段                   | 类型            | 说明                                      |
| -------------------- | ------------- | --------------------------------------- |
| `id`、`season_number` | integer       | 季 ID 及其在剧中的季号。                          |
| `name`、`overview`    | string        | 本地化文本，没有译文时为空。                          |
| `air_date`           | string 或 null | 第一集的日期，`YYYY-MM-DD`。                    |
| `poster_path`        | string 或 null | 拼接到 `https://img.amodb.dev/t/p/{size}`。 |
| `vote_average`       | number        | 这一季的汇总用户评分。                             |
| `episodes`           | array         | 每一集，字段见下一节。                             |

### 示例响应

`GET /3/tv/1396/season/1?language=en-US`。这一季有 7 集，所以 `episodes` 缩减为一条，该条目的 `crew` 和 `guest_stars` 数组也被省略：

```json
{
  "air_date": "2008-01-20",
  "episodes": [
    {
      "air_date": "2008-01-20",
      "episode_number": 1,
      "episode_type": "standard",
      "id": 62085,
      "name": "Pilot",
      "overview": "When an unassuming high school chemistry teacher discovers he has a rare form of lung cancer, he decides to...",
      "production_code": "",
      "runtime": 59,
      "season_number": 1,
      "show_id": 1396,
      "still_path": "/ydlY3iPfeOAvu8gVqrxPoMvzNCn.jpg",
      "vote_average": 0,
      "vote_count": 0
    }
  ],
  "name": "Season 1",
  "overview": "High school chemistry teacher Walter White's life is suddenly transformed by a dire medical diagnosis. Stre...",
  "id": 3572,
  "poster_path": "/91EIXdWxmp2mHNRsSuDW3WyDJLj.jpg",
  "season_number": 1,
  "vote_average": 0
}
```

季响应中的每一集还带有 `crew` 和 `guest_stars` 数组，条目结构见[演职员](https://amodb.dev/zh/credits/)。未播出单集的 `runtime` 可能为 null。

## 获取单集

`GET /3/tv/{series_id}/season/{season_number}/episode/{episode_number}` 返回一集。季响应已经包含所有带 `crew` 和 `guest_stars` 的单集，所以当你想在不重新加载整季的情况下刷新一条记录时，使用此接口。它接受 `language` 和 `append_to_response`，取值可为 `credits`、`external_ids`、`images`、`translations` 和 `videos`。像 `/3/tv/1396/season/1/episode/1?append_to_response=credits` 这样的请求只算一次请求。

| 字段                               | 类型             | 说明                                                                         |
| -------------------------------- | -------------- | -------------------------------------------------------------------------- |
| `id`                             | integer        | 单集 ID。                                                                     |
| `show_id`                        | integer        | 电视剧 ID。出现在季响应和电视剧的指针字段中，不在单集响应里。                                           |
| `season_number`、`episode_number` | integer        | 在剧中的位置。用它们构建请求路径。                                                          |
| `name`、`overview`                | string         | 本地化文本。                                                                     |
| `air_date`                       | string 或 null  | 仅日期，没有时间或时区。未排期时为 null。                                                    |
| `episode_type`                   | string         | 普通单集为 `standard`。要识别季终集，请把 `episode_number` 与电视剧记录中该季的 `episode_count` 比较。 |
| `runtime`                        | integer 或 null | 分钟。                                                                        |
| `production_code`                | string         | 制片厂编码，通常为空。                                                                |
| `still_path`                     | string 或 null  | 单集剧照。使用 `https://img.amodb.dev/t/p/{size}` 中的 `w300` 或 `original`。         |
| `vote_average`、`vote_count`      | number、integer | 汇总用户评分。                                                                    |
| `crew`、`guest_stars`             | array          | 这一集的演职员，见[演职员](https://amodb.dev/zh/credits/)。                             |

## 电视剧记录上的下一集和上一集

电视剧接口返回 `next_episode_to_air` 和 `last_episode_to_air`。每个都是单集摘要，字段同上，另外多了 `show_id`，少了 `crew` 和 `guest_stars`。已完结的剧和没有确认日期的在播剧，下一集为 null。示例读取在播剧《南方公园》。

```js
const headers = { Authorization: `Bearer ${process.env.AMODB_KEY}` };
const res = await fetch('https://api.amodb.dev/3/tv/2190', { headers });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const next = (await res.json()).next_episode_to_air;
console.log(next ? `Next: S${next.season_number}E${next.episode_number} on ${next.air_date}` : 'No upcoming episode');
```

做倒计时时，把 `air_date` 与访客的本地日期比较。该值不含具体时刻，所以请显示日期，而不是时钟时间。

## 季和单集的子资源

季和单集路径有自己的子资源。其中好几个目前返回空数据。

| 方法和路径                                                     | 返回内容                                          |
| --------------------------------------------------------- | --------------------------------------------- |
| `GET /tv/{series_id}/season/{season_number}/credits`      | 这一季的 `id`、`cast` 和 `crew`                     |
| `GET /tv/{series_id}/season/{season_number}/images`       | `id` 和空的 `posters` 数组                         |
| `GET /tv/{series_id}/season/{season_number}/videos`       | `id` 和空的 `results` 数组                         |
| `GET /tv/{series_id}/season/{season_number}/external_ids` | `id` 加上 `tvdb_id`、`wikidata_id` 等 ID，全部为 null |
| `GET /tv/{series_id}/season/{season_number}/translations` | `id` 和空的 `translations` 数组                    |
| `GET .../episode/{episode_number}/credits`                | `id`、空的 `cast`、`crew` 和 `guest_stars`         |
| `GET .../episode/{episode_number}/images`                 | `id` 和带有该单集剧照的 `stills` 数组                    |
| `GET .../episode/{episode_number}/videos`                 | `id` 和空的 `results` 数组                         |
| `GET .../episode/{episode_number}/external_ids`           | `id` 加上 `imdb_id`、`tvdb_id` 等 ID，全部为 null     |
| `GET .../episode/{episode_number}/translations`           | `id` 和 `translations` 数组，每种语言有名称和简介           |

季和单集的演职员见[演职员](https://amodb.dev/zh/credits/)，剧照见[图片](https://amodb.dev/zh/images-api/)。amodb 不提供 TMDB 的 `changes` 路径，也不提供返回某个单集分组中各集的接口。

## 错误

| HTTP | status_code | 何时出现                                    |
| ---- | ----------- | --------------------------------------- |
| 401  | 7           | 密钥缺失或错误。                                |
| 402  | 36          | 套餐周期已结束。                                |
| 404  | 34          | 电视剧、季或单集不存在，例如没有特别篇的剧请求第 `0` 季，或该路径不提供。 |
| 405  | 37          | 你调用了写入方法。                               |
| 429  | 25          | 超过每分钟请求数。请等待 `Retry-After` 秒。           |

完整的响应体格式见[错误](https://amodb.dev/zh/errors/)，各窗口见[速率限制](https://amodb.dev/zh/rate-limits/)。

## 实用说明

- 季和单集缓存 6 小时。剧集播出期间，每天重新读取当前季一两次即可。
- 一部 10 季的剧需要 10 次季调用。在每分钟 20 个请求的套餐上，这会用掉一分钟额度的一半，而 6 小时内重复运行时会由边缘缓存响应。
- 未播出的单集可能 `air_date` 为 null、`runtime` 为 null、`overview` 为空。请渲染占位内容，不要报错。
- 剧照是宽画幅，不是海报。列表中请求 `w300`，只有需要全尺寸查看时才用 `original`。

## 常见问题

### 如何列出一部剧的所有单集？

先从电视剧记录读取 number_of_seasons，再按每个季号调用一次季接口。每个季响应都带有完整的 episodes 数组，所以一部五季的剧需要五次请求。

### 季号 0 是什么意思？

第 0 季存放特别篇，例如幕后花絮和前情回顾。只有该剧有特别篇时它才存在，没有特别篇的剧请求第 0 季会返回 404 和 status_code 34。

### 为什么有些单集的 air_date 是 null？

该单集已公布但还没有确认日期。把 null 当作未排期，并在电视剧记录的 next_episode_to_air 中查看下一个有日期的单集。

### 能获得 DVD 顺序或绝对集数顺序吗？

不能作为单独的列表。GET /3/tv/{series_id}/episode_groups 会给出一部剧有哪些顺序，但 amodb 不提供返回某个分组单集的接口。季接口给出标准顺序。

## 相关页面

- [电视剧 API](https://amodb.dev/zh/tv-series/): 路径和 JSON 与 TMDB v3 相同的电视剧 API。按 ID 获取剧集的季、电视网和下一集，以及内容分级和观看平台。
- [演职员 API](https://amodb.dev/zh/credits/): 与 TMDB v3 路径和 JSON 相同的演职员 API。获取电影、剧集、季和集的演员与幕后人员，含 order、department 和 job 字段。
- [图片 API 与图片主机](https://amodb.dev/zh/images-api/): 采用 TMDB v3 路径的电影图片 API：列出作品的海报、背景图、标志和剧照，再从 img.amodb.dev 以 10 种尺寸无密钥加载。
- [剧集指南 API](https://amodb.dev/zh/episode-guide-api/): 基于 TMDB v3 季与集接口的剧集指南 API：任意剧集的片名、播出日期、时长、剧照和剧集分组列表，支持 14 种语言。
- [速率限制](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/tv-seasons-episodes/
