# API ключавых слоў

> Эндпоінты ключавых слоў вяртаюць тэгі сюжэта, прывязаныя да фільма або серыяла, кожны як id і назва. Можна знайсці адно ключавое слова, пералічыць фільмы з ім або перадаць id у discover праз with_keywords, каб пабудаваць тэматычныя старонкі. Фільмы адказваюць пад ключом keywords, серыялы пад ключом results.

## Эндпоінт

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

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

Адказ:

```json
{
  "id": 550,
  "keywords": [
    {
      "id": 818,
      "name": "based on novel or book"
    },
    {
      "id": 825,
      "name": "support group"
    },
    {
      "id": 851,
      "name": "dual identity"
    }
  ]
}
```

## Атрыманне ключавых слоў фільма

`GET /3/movie/{movie_id}/keywords` вяртае тэгі сюжэта аднаго фільма. Ключавое слова гэта кароткая фраза, напрыклад тэма, месца дзеяння або тып крыніцы, і яно мае лікавы id, які можна выкарыстоўваць у [пошуку па фільтрах](https://amodb.dev/be/discover/).

У прыкладзе загружаюцца ключавыя словы фільма, а потым у discover запытваюцца самыя папулярныя фільмы з тым жа першым ключавым словам.

```bash
curl -s "https://api.amodb.dev/3/movie/550/keywords" \
  -H "Authorization: Bearer $AMODB_KEY"

curl -s "https://api.amodb.dev/3/discover/movie?with_keywords=818&sort_by=popularity.desc" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const base = 'https://api.amodb.dev/3';
const headers = { Authorization: `Bearer ${process.env.AMODB_KEY}` };

const kw = await fetch(`${base}/movie/550/keywords`, { headers });
if (!kw.ok) throw new Error(`keywords failed with ${kw.status}`);
const { keywords } = await kw.json();
console.log(keywords.map((k) => `${k.id}:${k.name}`).join(', '));

if (keywords.length > 0) {
  const first = keywords[0];
  const found = await fetch(`${base}/discover/movie?with_keywords=${first.id}&sort_by=popularity.desc`, { headers });
  if (!found.ok) throw new Error(`discover failed with ${found.status}`);
  const page = await found.json();
  console.log(first.name, page.results.slice(0, 5).map((m) => m.title));
}
```

```python
import os
import requests

base = "https://api.amodb.dev/3"
headers = {"Authorization": f"Bearer {os.environ['AMODB_KEY']}"}

r = requests.get(f"{base}/movie/550/keywords", headers=headers, timeout=10)
r.raise_for_status()
keywords = r.json()["keywords"]
print(", ".join(f"{k['id']}:{k['name']}" for k in keywords))

if keywords:
    first = keywords[0]
    d = requests.get(
        f"{base}/discover/movie",
        params={"with_keywords": first["id"], "sort_by": "popularity.desc"},
        headers=headers,
        timeout=10,
    )
    d.raise_for_status()
    print(first["name"], [m["title"] for m in d.json()["results"][:5]])
```

```php
<?php
function amodb_get(string $url): array {
    $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");
    }
    return json_decode($body, true);
}

$base = 'https://api.amodb.dev/3';
$keywords = amodb_get("$base/movie/550/keywords")['keywords'];
if ($keywords) {
    $first = $keywords[0];
    $found = amodb_get("$base/discover/movie?with_keywords={$first['id']}&sort_by=popularity.desc");
    $titles = array_column(array_slice($found['results'], 0, 5), 'title');
    echo $first['name'], ': ', implode(', ', $titles), PHP_EOL;
}
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

var client = &http.Client{Timeout: 10 * time.Second}

func get(path string, out any) error {
	req, _ := http.NewRequest("GET", "https://api.amodb.dev/3"+path, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("amodb returned %s", res.Status)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

type Keywords struct {
	Keywords []struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"keywords"`
}

type Page struct {
	Results []struct {
		Title string `json:"title"`
	} `json:"results"`
}

func main() {
	var kw Keywords
	if err := get("/movie/550/keywords", &kw); err != nil {
		panic(err)
	}
	if len(kw.Keywords) == 0 {
		return
	}
	first := kw.Keywords[0]
	var page Page
	if err := get(fmt.Sprintf("/discover/movie?with_keywords=%d&sort_by=popularity.desc", first.ID), &page); err != nil {
		panic(err)
	}
	fmt.Println(first.Name)
	for i, m := range page.Results {
		if i == 5 {
			break
		}
		fmt.Println(" ", m.Title)
	}
}
```

### Параметры

| Параметр   | Дзе   | Абавязковы   | Апісанне                                                             |
| ---------- | ----- | ------------ | -------------------------------------------------------------------- |
| `movie_id` | шлях  | так          | Лікавы id фільма, напрыклад `550`.                                   |
| `api_key`  | запыт | адзін з двух | Ваш ключ. Выкарыстоўвайце яго або загаловак `Authorization: Bearer`. |

### Палі адказу

| Поле              | Тып      | Заўвагі                                                                                   |
| ----------------- | -------- | ----------------------------------------------------------------------------------------- |
| `id`              | цэлы лік | Id фільма.                                                                                |
| `keywords`        | масіў    | Аб’екты з `id` і `name`. Пусты, калі ў фільма іх няма.                                    |
| `keywords[].id`   | цэлы лік | Id ключавога слова. Выкарыстоўвайце яго ў `with_keywords` і ў шляхах ключавых слоў ніжэй. |
| `keywords[].name` | радок    | Тэкст ключавога слова.                                                                    |

### Прыклад адказу

```json
{
  "id": 550,
  "keywords": [
    { "id": 818, "name": "based on novel or book" },
    { "id": 825, "name": "support group" },
    { "id": 851, "name": "dual identity" }
  ]
}
```

Сапраўдны адказ змяшчае 14 ключавых слоў. Гэты скарочаны да першых 3. Назвы ключавых слоў не перакладаюцца, таму `language` тут не дзейнічае.

## Ключавыя словы серыяла

`GET /3/tv/{series_id}/keywords` прымае той жа ключ і вяртае тыя ж аб’екты, але масіў называецца `results`. Id у шляху гэта id серыяла, напрыклад `1396`.

```json
{
  "id": 1396,
  "results": [
    { "id": 1508, "name": "new mexico" },
    { "id": 2231, "name": "drug dealer" }
  ]
}
```

Сапраўдны адказ змяшчае 29 запісаў, а гэты скарочаны да першых 2. Кліент, які абслугоўвае абодва тыпы медыя, можа чытаць `data.keywords ?? data.results` і апрацоўваць іх аднолькава.

## Запісы ключавых слоў і іх фільмы

| Метад і шлях                       | Што вяртае                                                                          | Параметры          |
| ---------------------------------- | ----------------------------------------------------------------------------------- | ------------------ |
| `GET /keyword/{keyword_id}`        | `{ id, name }`                                                                      | няма               |
| `GET /keyword/{keyword_id}/movies` | Спіс фільмаў па старонках з `id`, `page`, `results`, `total_pages`, `total_results` | `language`, `page` |

Абодва шляхі выкарыстоўваюць лікавы id з масіва ключавых слоў. Спіс фільмаў дае ту ж форму запісу, што іншыя [спісы фільмаў](https://amodb.dev/be/trending-lists/), уключна з `genre_ids` і `poster_path`, плюс `id` ключавога слова зверху. `include_adult` прымаецца і ігнаруецца. Старонка пасля `total_pages` вяртае пусты масіў `results`. Спісу серыялаў для ключавога слова няма, таму для серыялаў выкарыстоўвайце `/discover/tv?with_keywords=`.

## Спалучэнне ключавых слоў з discover

Для ўсяго, што большае за простую выбарку, фільтруйце ў [пошуку па фільтрах](https://amodb.dev/be/discover/). Ён сартуе, ідзе па старонках і спалучае ключавыя словы з іншымі фільтрамі, а таксама ахоплівае серыялы разам з фільмамі.

| Мэта                                   | Радок запыту                                       |
| -------------------------------------- | -------------------------------------------------- |
| Фільмы з адным ключавым словам         | `/discover/movie?with_keywords=818`                |
| Фільмы з абодвума з двух ключавых слоў | `/discover/movie?with_keywords=818,4565`           |
| Фільмы без ключавога слова             | `/discover/movie?without_keywords=818`             |
| Серыялы з ключавым словам              | `/discover/tv?with_keywords=9712`                  |
| Серыялы з любым з двух ключавых слоў   | `/discover/tv?with_keywords=818%7C9712`            |
| Адно ключавое слова ўнутры жанру       | `/discover/movie?with_keywords=818&with_genres=18` |

Коска паміж id азначае ўсе з іх. Запішыце вертыкальную рыску паміж id, як у `with_keywords=818|4565`, каб супасці з любым. Вынікі discover кэшуюцца на edge на 30 хвілін.

## Пошук id ключавога слова

Каб ператварыць тэкст у id, выклічце `GET /3/search/keyword?query=heist`. Ён вяртае спіс пар `id` і `name` па старонках, па 20 на старонку. Поўны набор шляхоў пошуку гл. на старонцы [пошук](https://amodb.dev/be/search/). Пошук ключавых слоў, які нічога не знаходзіць, цяпер адказвае 500 са `status_code` 11 замест пустога спіса. Адказы пошуку кэшуюцца на 10 хвілін, але кожны выклік усё роўна ўлічваецца ў вашых запытах у хвіліну, таму адкладайце выклікі ў полі з падказкамі.

## Памылкі

| HTTP | status_code | Калі                                                                           |
| ---- | ----------- | ------------------------------------------------------------------------------ |
| 401  | 7           | Ключ адсутнічае або няправільны.                                               |
| 402  | 36          | Тэрмін дзеяння плана скончыўся.                                                |
| 404  | 34          | Няма ключавога слова з гэтым id у `/keyword/{keyword_id}` і яго спісе фільмаў. |
| 405  | 37          | Вы выкарысталі метад запісу на шляху ключавых слоў.                            |
| 429  | 25          | Вы перавысілі ліміт запытаў у хвіліну. Пачакайце `Retry-After` секунд.         |
| 500  | 11          | Запыт `/search/keyword`, які нічога не знаходзіць.                             |

Невядомы id фільма або серыяла на шляхах ключавых слоў не дае 404. Вы атрымліваеце HTTP 200 з id і пустым масівам.

Усе коды гл. на старонцы [памылкі](https://amodb.dev/be/errors/), а ліміты планаў на старонцы [ліміты запытаў](https://amodb.dev/be/rate-limits/).

## Практычныя заўвагі

- Захоўвайце `id` ключавога слова, а не назву. Назву могуць перафармуляваць, id не змяняецца.
- Спісы ключавых слоў кэшуюцца разам з дэталямі назвы, на 6 гадзін. Для старонак з тэгамі хопіць штотыднёвага абнаўлення.
- У адной назвы кароткі спіс тэгаў. Для тэматычных старонак пачынайце з id ключавога слова і пералічвайце яго назвы ў discover, а не наадварот.
- Выкарыстоўвайце `without_keywords`, каб прыбраць шум, напрыклад тэг, які вы ніколі не хочаце бачыць на сямейнай старонцы.
- Пусты масіў гэта нармальны адказ для малавядомых назваў. Хавайце радок з тэгамі замест таго, каб паказваць памылку.

## Пытанні і адказы

### Чаму адказ для серыялаў выкарыстоўвае results, а для фільмаў keywords?

Так TMDB v3 будуе іх форму, і amodb захоўвае гэта, каб існуючыя кліенты працавалі. Фільмы вяртаюць масіў keywords. Серыялы вяртаюць тыя ж аб’екты пад results. Чытайце data.keywords ?? data.results.

### Як знайсці id ключавога слова па яго назве?

Выклічце шлях пошуку ключавых слоў з запытам, напрыклад /3/search/keyword?query=heist. Ён вяртае спіс пар id і name па старонках. Абярыце дакладнае супадзенне і захавайце id.

### Ці можна выкарыстаць адзін id ключавога слова і для фільмаў, і для серыялаў?

Так. Id ключавога слова гэта адно і тое ж значэнне ў discover для фільмаў і ў discover для серыялаў, таму with_keywords працуе на абодвух шляхах. Захоўвайце адзін id на тэму і выкарыстоўвайце яго для абодвух тыпаў медыя.

### Як спалучыць некалькі ключавых слоў у адным запыце?

Злучайце id у with_keywords коскай, каб патрабаваць усе, або вертыкальнай рыскай, каб прыняць любы. Каб выключыць id, выкарыстоўвайце without_keywords. Трымайце URL у межах ліміту даўжыні вашага HTTP-кліента.

## Звязаныя старонкі

- [Discover API](https://amodb.dev/be/discover/): Discover API з фільтрамі TMDB v3. Спіс фільмаў і серыялаў па жанры, акцёрах, кампаніі, ключавым слове, годзе, рэйтынгу і працягласці з сартаваннем.
- [API пошуку](https://amodb.dev/be/search/): API пошуку з шляхамі і JSON як у TMDB v3. Фільмы, серыялы, персоны, калекцыі, кампаніі і ключавыя словы па тэксце, з фільтрамі па годзе і старонкамі.
- [API дэталяў фільма](https://amodb.dev/be/movies/): API дэталяў фільма з тымі ж шляхамі і JSON, што ў TMDB v3. Адзін фільм па id, а акцёры, рэйтынгі і настрой праз append_to_response, на 14 мовах.
- [API серыялаў](https://amodb.dev/be/tv-series/): API серыялаў з тымі ж шляхамі і JSON, што ў TMDB v3. Серыял па id з сезонамі, тэлеканаламі і наступным эпізодам, плюс узроставыя рэйтынгі і сэрвісы прагляду.
- [Жанры і даведачныя спісы](https://amodb.dev/be/genres/): API спіса жанраў з шляхамі TMDB v3: id жанраў фільмаў і серыялаў з перакладзенымі назвамі, а таксама спісы краін, моў, пасад, узроставых рэйтынгаў і сэрвісаў.
- [Хуткі старт](https://amodb.dev/be/quickstart/): Першы выклік да amodb: атрымайце ключ, запытайце дэталі фільма ў curl, JavaScript, Python, PHP або Go і загрузіце постэр з img.amodb.dev. Менш за 2 хвіліны.

Абноўлена: 2026-10-18. Source: https://amodb.dev/be/keywords/
