# API ключових слів

> Ендпоінти ключових слів повертають теги сюжету фільму або серіалу, кожен у вигляді id та name. Можна отримати одне ключове слово, список фільмів із ним або передати 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, який можна повторно використати в [discover](https://amodb.dev/uk/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/uk/trending-lists/), зокрема `genre_ids` і `poster_path`, а також `id` ключового слова нагорі. `include_adult` приймається й ігнорується. Сторінка за межами `total_pages` повертає порожній масив `results`. Списку серіалів за ключовим словом немає, тому для TV використовуйте `/discover/tv?with_keywords=`.

## Поєднання ключових слів з discover

Для всього, що складніше за простий список, фільтруйте в [discover](https://amodb.dev/uk/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 кешуються на периферії мережі на 30 хвилин.

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

Щоб перетворити текст на id, викличіть `GET /3/search/keyword?query=heist`. Він повертає посторінковий список пар `id` і `name`, по 20 на сторінку. Повний набір шляхів пошуку дивіться на сторінці [пошук](https://amodb.dev/uk/search/). Пошук ключового слова без збігів наразі відповідає 500 зі `status_code` 11 замість порожнього списку. Відповіді пошуку кешуються на 10 хвилин, але кожен виклик усе одно враховується в ліміті запитів на хвилину, тому додайте затримку введення (debounce) для поля з підказками.

## Помилки

| 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/uk/errors/), а ліміти тарифів на сторінці [ліміти запитів](https://amodb.dev/uk/rate-limits/).

## Практичні поради

- Зберігайте `id` ключового слова, а не назву. Назву можуть переформулювати, id не змінюється.
- Списки ключових слів кешуються разом з деталями фільму чи серіалу на 6 годин. Щотижневого оновлення для сторінок тегів цілком досить.
- Один фільм має короткий список тегів. Для тематичних сторінок починайте з id ключового слова й перелічуйте його фільми в discover, а не навпаки.
- Використовуйте `without_keywords`, щоб прибрати шум, наприклад тег, якого ви ніколи не хочете бачити на сімейній сторінці.
- Порожній масив є коректною відповіддю для маловідомих фільмів. Приховайте рядок тегів замість показу помилки.

## Запитання й відповіді

### Чому у відповіді для серіалів results, а для фільмів keywords?

Так відповідь будує TMDB v3, і amodb зберігає цю форму, щоб наявні клієнти працювали. Фільми повертають масив keywords. Серіали повертають такі самі об'єкти в results. Читайте data.keywords ?? data.results.

### Як знайти id ключового слова за його назвою?

Викличіть шлях пошуку ключових слів із параметром query, наприклад /3/search/keyword?query=heist. Він повертає посторінковий список пар id і name. Виберіть точний збіг і збережіть id.

### Чи можна використовувати один id ключового слова і для фільмів, і для серіалів?

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

### Як поєднати кілька ключових слів в одному запиті?

З'єднайте id у with_keywords комою, щоб вимагати всі, або вертикальною рискою, щоб прийняти будь-який. Використовуйте without_keywords, щоб виключити id. Тримайте URL у межах ліміту довжини вашого HTTP-клієнта.

## Пов'язані сторінки

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

Оновлено: 2026-10-18. Source: https://amodb.dev/uk/keywords/
