ember API · v1

Перенеси свои фильмы
на ember

Отметки просмотренного, оценки и списки — из любого приложения, с любой площадки. Один ключ, один запрос: пришли ID, ссылку или просто название — разберёмся сами.

Начало

API — это способ сказать ember «этот фильм просмотрен» из любого другого места: из приложения, которым ты смотришь кино, из выгрузки другого сервиса, из своего скрипта.

Кинопоиск IMDb Letterboxd Trakt свой скрипт, CSV
PUT /api/v1/entries найдём фильм по ID,
ссылке или названию
просмотрено оценка 8.5 хочу посмотреть

Фильм или игру при этом можно указать как угодно — imdb_id, steam_appid, ссылка на Кинопоиск, Steam или Trakt, свой внутренний ID приложения, просто название с годом (для игр добавь "media_type": "game"). Следующий раздел — про это.

API-ключ

Создай ключ в настройках → API-ключи. Показывается один раз — нажми на него, и он скопируется.

Подставляй его в заголовок Authorization каждого запроса:

заголовок каждого запроса
Authorization: Bearer ember_xxxxxxxx

Вместо Authorization можно слать заголовок X-Api-Key: ember_xxxxxxxx — это то же самое.

Эндпоинты

Список эндпоинтов. Готовые примеры на Python и curl: скопируй, подставь свой ключ и домен.

GET/api/v1/meпроверка ключа — кто ты и какой токен
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/me", headers=H)
print(r.json())
curl · одной строкой
curl.exe "https://ТВОЙ-САЙТ/api/v1/me" -H "Authorization: Bearer ember_xxxxxxxx"
GET/api/v1/lookup?imdb_id=tt0110912найти фильм, ничего не записывая — проверить сопоставление
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/lookup", headers=H,
    params={"imdb_id": "tt0110912"})
print(r.json())
curl · одной строкой
curl.exe "https://ТВОЙ-САЙТ/api/v1/lookup?imdb_id=tt0110912" -H "Authorization: Bearer ember_xxxxxxxx"
GET/api/v1/search?q=матрицапоиск по каталогу ember
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/search", headers=H,
    params={"q": "матрица"})
print(r.json())
curl · одной строкой
curl.exe "https://ТВОЙ-САЙТ/api/v1/search?q=матрица" -H "Authorization: Bearer ember_xxxxxxxx"
GET/api/v1/entriesтвоя библиотека: ?status=watched|want|watching|rated, ?since=, ?limit=&offset=
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/entries", headers=H,
    params={"status": "watched", "limit": 50})
print(r.json())
curl · одной строкой
curl.exe "https://ТВОЙ-САЙТ/api/v1/entries?status=watched" -H "Authorization: Bearer ember_xxxxxxxx"
PUT/api/v1/entriesсоздать или обновить запись — главная точка входа
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "imdb_id": "tt0133093",
    "status": "watched",
    "rating": 8.5,
    "watched_on": "2026-10-03",
})
print(r.json())
curl · одной строкой
curl.exe -X PUT "https://ТВОЙ-САЙТ/api/v1/entries" -H "Authorization: Bearer ember_xxxxxxxx" -H "Content-Type: application/json" -d '{"imdb_id":"tt0133093","status":"watched","rating":8.5}'
POST/api/v1/entries/batchпакет до 100 записей — массовый перенос
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.post(f"{API}/entries/batch", headers=H,
    json={"items": [
        {"imdb_id": "tt0133093", "status": "watched", "rating": 8.5},
        {"url": "https://letterboxd.com/film/parasite/", "status": "want"},
        {"title": "Мой личный фильм", "allow_manual": True},
    ]})
print(r.json())
curl · одной строкой
curl.exe -X POST "https://ТВОЙ-САЙТ/api/v1/entries/batch" -H "Authorization: Bearer ember_xxxxxxxx" -H "Content-Type: application/json" -d '{"items":[{"imdb_id":"tt0133093","status":"watched","rating":8.5}]}'
DELETE/api/v1/entries/{kp_id}удалить запись из библиотеки
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.delete(f"{API}/entries/326", headers=H)
print(r.status_code)  # 204 — удалено
curl · одной строкой
curl.exe -X DELETE "https://ТВОЙ-САЙТ/api/v1/entries/326" -H "Authorization: Bearer ember_xxxxxxxx"

Как сослаться на фильм

Разбор примера готового запроса

python · главный запрос · нажимай на цифры
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "imdb_id": "tt0133093",           1
    "status": "watched",                 2
    "rating": 8.5,                        3
    "watched_on": "2026-10-03",            4
    "allow_manual": True,                 5
    "poster": "https://…/cover.jpg",       6
})
print(r.json())
1

Как указать фильм — любой из вариантов

Пришли что есть — API сам выберет лучший вариант по приоритету:

kp_idнадёжнее всего

ID Кинопоиска: 326 → «Побег из Шоушенка». Работает всегда, даже для фильма, которого ещё нет на ember.

imdb_idуниверсальный

tt0110912 → «Криминальное чтиво». Работает для любого фильма мира: если его нет в базе ember, название и год подтянутся из открытого каталога, а фильм найдётся на Кинопоиске с проверкой imdbId.

urlпроще всего

Просто ссылка на страницу фильма: kinopoisk.ru, imdb.com, letterboxd.com, themoviedb.org, trakt.tv. ID вытащится из самой ссылки.

title + yearзапасной

Поиск по названию — для экспортов, где нет никаких ID (например, CSV из Letterboxd). year можно не передавать — без него найдём по одному названию, но с годом точнее.

trakt_idtrakt

23456 — числовой ID Trakt. Резолвится через их API в imdb_id и запоминается навсегда: дальше работает мгновенно.

externalсвой id

{"source": "myapp", "id": "42"} — ID с твоей площадки. При первом запросе разрешается по остальным полям, а соответствие запоминается: дальше он работает мгновенно. Название source придумай сам — это неймспейс твоей интеграции.

2

"status" — что записать

"watched"просмотрено

Фильм просмотрен (с оценкой или без), убирается из «хочу посмотреть».

"want"хочу

В избранное со статусом «хочу посмотреть».

"watching"смотрю

В избранное со статусом «смотрю сейчас».

"none"убрать

Удалить запись из библиотеки целиком.

3

"rating" — твоя оценка

Число от 0 до 10. null снимает оценку — фильм при этом остаётся просмотренным. Оценка сама по себе отмечает фильм просмотренным, даже без "status".

4

"watched_on" — когда посмотрел

Дата просмотра в формате ГГГГ-ММ-ДД. Попадёт в дневник на сайте — при переносе истории из другого сервиса даты сохранятся как были.

5

"allow_manual" — если фильма нет нигде

true — если фильм не нашёлся ни в одном каталоге, вместо ошибки создаётся ручная карточка (как «Свой фильм» на сайте). При массовом переносе ни одна строка не потеряется. Работает только на запись.

6

"poster" — обложка ручной карточки

Ссылка на картинку (http/https). Применяется, только когда создаётся ручная карточка — то есть вместе с "allow_manual": true и ненайденным фильмом. Чужие постеры не перезаписывает.

В каждом ответе поле resolved_via показывает, как фильм нашёлся: kp_id, imdb:cinemeta, url:kinopoisk, external:myapp, title, manual…

Отметить фильм (upsert)

запрос
PUT /api/v1/entries
Authorization: Bearer ember_xxxxxxxx

{
  "imdb_id": "tt0133093",
  "status": "watched",
  "rating": 8.5,
  "watched_on": "2026-10-03",
  // опционально:
  "allow_manual": true,
  "poster": "https://…"  // обложка ручной карточки
}
ответ · 200
{
  "ok": true,
  "resolved_via": "imdb:cinemeta",
  "entry": {
    "work": {
      "kp_id": 326,
      "title": "Матрица",
      "year": 1999
    },
    "status": "watched",
    "watched": true,
    "score": 8.5,
    "watched_on": "2026-10-03"
  }
}

Что значит status

"watched"просмотрено

Фильм просмотрен (с оценкой или без), убирается из «хочу посмотреть».

"want"хочу

В избранное со статусом «хочу посмотреть».

"watching"смотрю

В избранное со статусом «смотрю сейчас».

"none"убрать

Удалить запись из библиотеки целиком.

rating: 0–10; null снимает оценку (просмотренным фильм остаётся). Оценка сама по себе отмечает фильм просмотренным. watched_on — дата просмотра в формате ГГГГ-ММ-ДД. Повторный запрос — идемпотентен: просто обновит поля.

Массовый перенос

До 100 фильмов за запрос. Каждый элемент — как в upsert. Ошибка в одной строке не роняет остальные.

python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.post(f"{API}/entries/batch", headers=H, json={
    "items": [
        {"imdb_id": "tt0133093", "status": "watched", "rating": 8.5},
        {"url": "https://letterboxd.com/film/alien/", "status": "watched"},
        {"title": "Город грехов", "year": 2005, "status": "want"},
    ]
})
print(r.json())
curl · одной строкой
curl.exe -X POST "https://ТВОЙ-САЙТ/api/v1/entries/batch" -H "Authorization: Bearer ember_xxxxxxxx" -H "Content-Type: application/json" -d '{"items":[{"imdb_id":"tt0133093","status":"watched","rating":8.5},{"title":"Город грехов","year":2005,"status":"want"}]}'

Что придёт в ответ:

ответ · 200
{
  "ok": true,
  "total": 4,
  "applied": 4,
  "aborted": false,
  "results": [
    {"index": 0, "ok": true,
     "resolved_via": "imdb:cinemeta", …},
    {"index": 1, "ok": true,
     "resolved_via": "url:letterboxd", …},
    …
  ]
}

У ошибок бывает "retryable": true — значит, кончилась дневная квота Кинопоиска или временный сбой: повтори позже, успешные строки не потеряются. Если обработка не уложилась в дедлайн, в ответе будет "aborted": true, а остатку — ошибка deadline: просто отправь необработанные элементы повторным пакетом.

Ошибки и лимиты

Формат ошибки: {"ok": false, "error": {"code": "…", "message": "…"}}

unauthorized401

Ключ не передан, отозван или аккаунт отключён.

validation_error400 / 413

Кривые поля: плохой status, рейтинг вне 0–10, нечитаемый JSON, слишком длинные строки (title ≤ 300, url ≤ 2048). 413 — тело больше 256 КБ.

unsupported_url400

Ссылка с неизвестного домена — пришли imdb_id или название.

not_found / resolution_failed404

Фильм не сопоставился. С retryable: true — повтори позже.

manual_limit400

Исчерпана суточная квота ручных карточек (200/день) — это защита каталога от спама.

deadlineв batch

Пакет оборван по дедлайну — элемент не обработан, отправь его повторным пакетом.

rate_limited429

Превышен лимит — подожди и повтори. Вернётся и при флуде невалидными ключами.

Лимиты

Лимиты считаются на пользователя, а не на ключ — несколько ключей общую квоту не увеличивают.

120запросов чтения / мин
60запросов записи / мин
10пакетов / 10 мин

Перенос фильмов

Существует много способов перенести фильмы с других площадок на ember:

самый эффективный

CSV-файл

Вся библиотека разом — оценки, даты просмотров, списки «хочу» и «смотрю».

  • Выгрузи CSV — инструкции для каждой площадки прилагаются
  • Загрузи его — и мы сопоставим сами
Перенести файлом →
удобно на ПК

Закладка

Фильм добавляется в один клик прямо со страницы Кинопоиска, IMDb или Letterboxd.

+ в ember
Как добавить на компьютере
  1. Покажи панель закладок: Ctrl+Shift+B, в Safari — ⌘+Shift+B («Вид → Показать панель избранного»).
  2. Зажми кнопку «+ в ember» выше и перетащи её на эту панель.
  3. Готово — она останется там навсегда.

Как пользоваться: открыл страницу фильма на Кинопоиске, IMDb или Letterboxd → жмёшь закладку + в ember → тебя переносит на ember, фильм уже найден → жми «Добавить».

Добавление по ссылке

Один фильм за раз — прямо отсюда, без переходов:

Разработчикам: то же самое автоматизируется через API — ключ в настройках, готовые сниппеты в «Примеры кода».

Примеры кода

Все возможности API в готовых запросах — выбери категорию и язык, скопируй, подставь домен и ключ.

Проверить ключ — кто тыGET /api/v1/me
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/me", headers=H)
print(r.json())
Просмотрен + оценка + датаPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "imdb_id": "tt0133093",
    "status": "watched",
    "rating": 8.5,
    "watched_on": "2026-10-03",
})
print(r.json())
В «хочу посмотреть» по ссылкеPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "url": "https://letterboxd.com/film/parasite/",
    "status": "want",
})
print(r.json())
«Смотрю сейчас» по ID КинопоискаPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "kp_id": 326,
    "status": "watching",
})
print(r.json())
Отметить по ссылке на КинопоискPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "url": "https://www.kinopoisk.ru/film/326/",
    "status": "watched",
    "rating": 9,
})
print(r.json())
Отметить по названию и годуPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "title": "Матрица",
    "year": 1999,    # year можно опустить
    "status": "watched",
})
print(r.json())
Свой ID приложения — externalPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "external": {"source": "myapp", "id": "42"},
    "title": "Обитель зла",
    "year": 2002,
    "status": "watched",
})
print(r.json())

Соответствие myapp:42 → фильм запомнится: дальше можно слать один external.

Ручная карточка с обложкойPUT /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.put(f"{API}/entries", headers=H, json={
    "title": "Мой личный фильм о котиках",
    "year": 2024,
    "status": "watched",
    "allow_manual": True,
    "poster": "https://example.com/cover.jpg",
})
print(r.json())

Фильм не нашёлся нигде → вместо ошибки создаётся ручная карточка.

Проверить сопоставление (lookup)GET /api/v1/lookup
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/lookup", headers=H,
    params={"imdb_id": "tt0110912"})
print(r.json())

Ничего не записывает — только показывает, что за фильм найдётся.

Поиск по каталогу emberGET /api/v1/search
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/search", headers=H,
    params={"q": "матрица"})
print(r.json())
Моя библиотека — все просмотренныеGET /api/v1/entries
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/entries", headers=H,
    params={"status": "watched", "limit": 50})
print(r.json())

Статусы для фильтра: watched, want, watching, rated.

Только изменения с датыGET /api/v1/entries?since=…
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.get(f"{API}/entries", headers=H,
    params={"since": "2026-01-01"})
print(r.json())
Массовый перенос — до 100 за разPOST /api/v1/entries/batch
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.post(f"{API}/entries/batch", headers=H,
    json={"items": [
        {"imdb_id": "tt0133093", "status": "watched", "rating": 8.5},
        {"url": "https://letterboxd.com/film/parasite/", "status": "want"},
        {"title": "Мой личный фильм", "allow_manual": True},
    ]})
print(r.json())

Ошибка в одной строке не роняет остальные — в ответе результат по каждой.

Удалить запись из библиотекиDELETE /api/v1/entries/{kp_id}
python
import requests

API = "https://ТВОЙ-САЙТ/api/v1"
KEY = "ember_xxxxxxxx"
H = {"Authorization": f"Bearer {KEY}"}

r = requests.delete(f"{API}/entries/326", headers=H)
print(r.status_code)  # 204 — удалено

То же самое делает PUT /entries со "status": "none".