# Посібник з Python API фільмів

> Створіть невеликий консольний інструмент Python, який приймає назву фільму, шукає її, а потім одним додатковим викликом отримує деталі, акторів і рейтинги. Потрібні Python 3.10 або новіший, бібліотека requests і ключ amodb. Це займає близько 15 хвилин і завершується повним скриптом.

Ви створите `movie.py`, консольний інструмент. Ви передаєте назву, він шукає, бере перший збіг і друкує рік, тривалість, жанри, головних акторів, рейтинг IMDb і URL постера. Потрібні Python 3.10 або новіший, бібліотека `requests` і ключ amodb. Заплануйте 15 хвилин.

## Крок 1: отримайте ключ API

amodb працює за передплатою без безкоштовного тарифу, тож спершу оберіть тариф на сторінці [ціни](https://amodb.dev/uk/pricing/). Тарифи на 20, 40 і 120 запитів на хвилину повертають однакові дані. Потім створіть ключ у панелі облікового запису. Ключ складається з 32 шістнадцяткових символів малими літерами.

Експортуйте його як змінну середовища, щоб він не потрапив у ваш вихідний код:

```bash
export AMODB_KEY="YOUR_API_KEY"
```

У Windows PowerShell той самий рядок виглядає як `$env:AMODB_KEY = "YOUR_API_KEY"`.

## Крок 2: налаштуйте проєкт

Створіть віртуальне середовище й встановіть `requests`:

```bash
python -m venv .venv
source .venv/bin/activate
pip install requests
```

У Windows замість другого рядка виконайте `.venv\Scripts\activate`.

## Крок 3: надішліть перший запит

Ендпоінт пошуку це `GET /3/search/movie` з параметром `query`. Цей фрагмент друкує статус HTTP та id найкращого збігу. Завжди передавайте тайм-аут, бо `requests` за замовчуванням чекає вічно.

```python
import os
import requests

res = requests.get(
    "https://api.amodb.dev/3/search/movie",
    params={"query": "fight club", "language": "en-US"},
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
print(res.status_code, res.json()["results"][0]["id"])
```

Відповідь має формат TMDB: масив `results`, відсортований за релевантністю, а також `page` і `total_results`. Усі фільтри дивіться на сторінці [пошук](https://amodb.dev/uk/search/).

## Крок 4: отримайте деталі, акторів і рейтинги

Результати пошуку короткі. Використайте id із `GET /3/movie/{movie_id}` і додайте `append_to_response=credits,ratings`, щоб отримати акторський склад та оцінки IMDb, Kinopoisk, Metacritic, Rotten Tomatoes і TMDB в одному запиті. Кожна оцінка це об'єкт із `value` і `votes` або `null`, коли джерело невідоме, тому перевіряйте перед друком. Подробиці на сторінках [фільми](https://amodb.dev/uk/movies/) та [рейтинги](https://amodb.dev/uk/ratings/). У фінальному скрипті `session` це `requests.Session`, що вже несе заголовок Authorization.

```python
params = {"language": "en-US", "append_to_response": "credits,ratings"}
movie = session.get(f"{BASE}/movie/{movie_id}", params=params, timeout=10).json()
imdb = (movie.get("ratings") or {}).get("imdb")
```

## Крок 5: обробка помилок і лімітів запитів

Перевіряйте статус, перш ніж розбирати тіло. Помилки використовують тіло TMDB з `status_code` і `status_message`.

| Статус      | Значення                          | Що робить скрипт                                                         |
| ----------- | --------------------------------- | ------------------------------------------------------------------------ |
| 401         | Хибний або призупинений ключ      | Зупиняється й радить перевірити `AMODB_KEY`. Повтор не допоможе.         |
| 404         | Такого id немає                   | Друкує `status_message`.                                                 |
| 429         | Перевищено ваші запити на хвилину | Спить `Retry-After` секунд (не більше 30), потім повторює до двох разів. |
| 504         | Тайм-аут бекенда                  | Друкується як помилка. Запустіть команду ще раз.                         |
| Збій мережі | Немає з'єднання або тайм-аут      | Перехоплюється як `requests.RequestException` і повідомляється.          |

Цикл повторів короткий:

```python
for attempt in range(3):
    res = session.get(url, params=params, timeout=10)
    if res.status_code != 429 or attempt == 2:
        break
    time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))
```

Більше кодів перелічено на сторінці [помилки](https://amodb.dev/uk/errors/), а правила вікна описано на сторінці [ліміти запитів](https://amodb.dev/uk/rate-limits/).

## Крок 6: кешуйте відповіді на диску

Кеш на периферії зберігає деталі 6 годин, а пошук 10 хвилин. Ваш власний кеш економить обхід мережі й не витрачає жодного запиту з вашого ліміту. Фінальний скрипт зберігає кожну відповідь у файлі з назвою на основі хешу шляху й параметрів і використовує файл повторно, поки він молодший за TTL.

## Крок 7: повна програма

Збережіть це як `movie.py` і запустіть `python movie.py "fight club"`.

```python
import hashlib
import json
import os
import sys
import time
from pathlib import Path

import requests

BASE = "https://api.amodb.dev/3"
POSTER = "https://img.amodb.dev/t/p/w342"
CACHE_DIR = Path.home() / ".cache" / "amodb-movie-cli"
SEARCH_TTL = 10 * 60
DETAILS_TTL = 6 * 60 * 60


class ApiError(Exception):
    pass


def error_message(res):
    try:
        return res.json()["status_message"]
    except (ValueError, KeyError):
        return res.reason


def get(session, path, params, ttl):
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    stamp = f"{path}?{json.dumps(params, sort_keys=True)}"
    cache_file = CACHE_DIR / (hashlib.sha256(stamp.encode()).hexdigest() + ".json")
    if cache_file.exists() and time.time() - cache_file.stat().st_mtime < ttl:
        return json.loads(cache_file.read_text(encoding="utf-8"))

    for attempt in range(3):
        try:
            res = session.get(BASE + path, params=params, timeout=10)
        except requests.RequestException as exc:
            raise ApiError(f"network error: {exc}") from exc
        if res.status_code != 429 or attempt == 2:
            break
        time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))

    if res.status_code == 401:
        raise ApiError("401: the key is invalid or suspended. Check AMODB_KEY.")
    if res.status_code != 200:
        raise ApiError(f"{res.status_code}: {error_message(res)}")
    cache_file.write_text(res.text, encoding="utf-8")
    return res.json()


def search_movie(session, title):
    data = get(session, "/search/movie", {"query": title, "language": "en-US"}, SEARCH_TTL)
    if not data["results"]:
        raise ApiError(f"no movie found for {title!r}")
    return data["results"][0]["id"]


def movie_details(session, movie_id):
    params = {"language": "en-US", "append_to_response": "credits,ratings"}
    return get(session, f"/movie/{movie_id}", params, DETAILS_TTL)


def print_card(movie):
    year = (movie.get("release_date") or "")[:4] or "unknown year"
    genres = ", ".join(g["name"] for g in movie["genres"])
    cast = ", ".join(p["name"] for p in movie["credits"]["cast"][:3])
    imdb = (movie.get("ratings") or {}).get("imdb")
    print(f"{movie['title']} ({year}), {movie.get('runtime') or '?'} min")
    print(f"Genres: {genres or 'none listed'}")
    print(f"Cast: {cast or 'none listed'}")
    print(f"IMDb: {imdb['value']} ({imdb['votes']} votes)" if imdb else "IMDb: not known")
    if movie.get("poster_path"):
        print(f"Poster: {POSTER}{movie['poster_path']}")
    print(movie.get("overview") or "No overview in this language.")


def main():
    if len(sys.argv) < 2:
        sys.exit('Usage: python movie.py "fight club"')
    key = os.environ.get("AMODB_KEY")
    if not key:
        sys.exit("Set AMODB_KEY first.")
    session = requests.Session()
    session.headers["Authorization"] = f"Bearer {key}"
    try:
        movie_id = search_movie(session, " ".join(sys.argv[1:]))
        print_card(movie_details(session, movie_id))
    except ApiError as exc:
        sys.exit(f"error: {exc}")


if __name__ == "__main__":
    main()
```

## Наступні кроки

- Надрукуйте постер в іншому розмірі. Хост зображень приймає будь-який з розмірів зі сторінки [API зображень](https://amodb.dev/uk/images-api/) і не потребує ключа.
- Додайте фільтр `year` у виклик пошуку або перейдіть на [discover](https://amodb.dev/uk/discover/) для відфільтрованих списків.
- Покажіть шкалу настрою поруч із рейтингом, додавши `moods` до `append_to_response`. Кожен запис у `moods.results` має `name` і `avg` за шкалою від 0 до 10.
- Прочитайте [швидкий старт](https://amodb.dev/uk/quickstart/) про той самий потік у curl, потім спробуйте [посібник із JavaScript](https://amodb.dev/uk/javascript-movie-api/) для пакетної версії.

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

### Яка версія Python і які бібліотеки потрібні?

Python 3.10 або новіший і бібліотека requests. Усе інше надає стандартна бібліотека. Ключ передається в заголовку Authorization Bearer, тому він ніколи не потрапляє в URL, історію оболонки чи журнали сервера.

### Як лишатися в межах ліміту запитів?

Кешуйте відповіді й дотримуйтесь Retry-After. Кожен ключ отримує 20, 40 або 120 запитів на хвилину в ковзному 60-секундному вікні. Скрипт повторює 429 двічі, чекає кількість секунд із заголовка й обмежує очікування 30 секундами.

### Чи можна використати код, написаний для клієнта TMDB v3?

Так, для викликів читання. Шляхи, параметри й JSON збігаються з TMDB v3, тож змініть базовий URL на https://api.amodb.dev/3 і надсилайте ключ amodb. Методи запису, як-от оцінювання фільму, повертають 405 зі status_code 37.

### Куди класти ключ: в api_key чи в заголовок?

Працюють обидва способи. Заголовок тримає ключ поза URL, журналами проксі та рядками запитів, тому цей посібник використовує Authorization: Bearer. Параметр запиту api_key лишається для інструментів, що не вміють задавати заголовки.

### Як довго кешувати пошук і деталі?

10 хвилин для пошуку й 6 годин для деталей, тобто стільки ж, скільки живе кеш на периферії. Коротший локальний кеш лише додає запити, бо копія на периферії спільна для всіх ключів і вже має ці терміни.

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

- [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 мовами.
- [Рейтинги фільмів і серіалів](https://amodb.dev/uk/ratings/): API рейтингів фільмів повертає оцінки IMDb, Kinopoisk, Metacritic, Rotten Tomatoes і TMDB для фільму чи серіалу одним викликом, з null для невідомих джерел.
- [Швидкий старт](https://amodb.dev/uk/quickstart/): Перший виклик amodb: отримайте ключ, запросіть деталі фільму в curl, JavaScript, Python, PHP чи Go і завантажте постер з img.amodb.dev. Менш ніж за 2 хвилини.
- [Ліміти запитів](https://amodb.dev/uk/rate-limits/): amodb обмежує кожен ключ запитами на хвилину в ковзному вікні 60 секунд: 20, 40 або 120 за тарифом. Читайте заголовки ліміту й обробляйте 429 із Retry-After.
- [Помилки](https://amodb.dev/uk/errors/): Усі помилки amodb зі статусом HTTP, status_code і повідомленням TMDB, а також форма помилки OMDb. Що означає кожна й що має робити ваш клієнт далі.

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