TrendNews API

REST API для читання постів-дайджестів і трендів новин по 111 країнах. Формат — JSON, кодування UTF-8, увесь час — у UTC.

Швидкий старт

Базова адреса: https://trendnews.uk.app/api/v1. Зареєструйтесь, створіть API-ключ у кабінеті й надішліть перший запит:

curl -H "X-API-Key: ВАШ_КЛЮЧ" "https://trendnews.uk.app/api/v1/posts?country=UA&limit=5"

Країни задаються кодом ISO-2 (UA, US, DE, PL…). Повний список із мовами стрічок повертає /api/v1/countries. Нові пости з'являються щогодини.

Авторизація

Кожен запит, крім /v1/health, має містити API-ключ у заголовку:

X-API-Key: tn_live_…

Також приймається Authorization: Bearer tn_live_…. Передавати ключ у параметрах URL не можна: так він потрапив би в логи та історію браузера.

Тримайте ключ на своєму сервері. Якщо вбудувати його в JavaScript на сторінці, будь-який відвідувач зможе його скопіювати. Запити з фронтенду робіть через свій бекенд.

Ліміти

Типово 120 запитів на хвилину на ключ. Поточний стан повертається в заголовках кожної відповіді:

ЗаголовокЗначення
X-RateLimit-Limitліміт запитів на хвилину для вашого ключа
X-RateLimit-Remainingскільки запитів лишилося в поточній хвилині
Retry-Afterлише з відповіддю 429: через скільки секунд можна повторити

Відповіді можна кешувати до 60 секунд (Cache-Control: private, max-age=60): дані оновлюються раз на годину.

Формат відповіді та помилки

Успішна відповідь завжди має поле data, списки — ще й meta з пагінацією. Помилка має поле error з машинним кодом і поясненням.

// успіх
{ "data": [ … ], "meta": { "total": 856, "limit": 20, "offset": 0, "has_more": true } }

// помилка
{
  "error": {
    "code": "invalid_country",
    "message": "Unknown country code. The list of supported ISO-2 codes: /api/v1/countries"
  }
}
HTTPcodeКоли виникає
400missing_paramне передано обов'язковий параметр
400invalid_param, invalid_country, invalid_dateпараметр має неправильне значення або формат
401missing_api_keyзапит без ключа
401invalid_api_keyключ неправильний або відкликаний
403account_blockedобліковий запис власника ключа заблоковано
404not_foundневідомий ендпоінт або пост не існує
405method_not_allowedметод, відмінний від GET
429rate_limitedперевищено ліміт запитів
500server_errorвнутрішня помилка сервера

Пагінація

Списки повертають не більше limit записів (до 100). Щоб отримати наступну сторінку, збільште offset на limit, поки meta.has_more дорівнює true.

Ендпоінти

GET /api/v1/health без ключа

Стан сервісу

Перевірка, що API працює, і час останнього опублікованого поста. Ключ не потрібен — зручно для моніторингу.

Приклад відповіді
{
  "data": {
    "status": "ok",
    "last_post_at": "2026-10-24T14:05:09Z",
    "countries": 6,
    "time": "2026-10-24T14:31:38Z"
  }
}
GET /api/v1/countries потрібен ключ

Країни та мови

Усі країни, для яких збираються новини: код ISO-2, назва, прапор, мова стрічки, кількість постів і дата останнього.

Приклад відповіді
{
  "data": [
    { "code": "UA", "name": "Україна", "flag": "🇺🇦", "lang": "uk", "post_count": 856, "last_post_date": "2026-10-24" },
    { "code": "SI", "name": "Словенія", "flag": "🇸🇮", "lang": "sl", "post_count": 150, "last_post_date": "2026-10-24" },
    { "code": "GB", "name": "Велика Британія", "flag": "🇬🇧", "lang": "en", "post_count": 284, "last_post_date": "2026-10-24" }
  ]
}
GET /api/v1/categories потрібен ключ

Категорії країни

Категорії постів обраної країни з кількістю постів у кожній. Назви категорій — мовою країни.

ПараметрОбов'язковийОпис
countryтакКод країни ISO-2 (повний список — /v1/countries)
Приклад відповіді
{
  "data": [
    { "slug": "politics", "label": "Politics", "posts": 61 },
    { "slug": "sport", "label": "Sport", "posts": 54 },
    { "slug": "other", "label": "Other", "posts": 48 }
  ],
  "meta": { "country": "GB" }
}
GET /api/v1/posts потрібен ключ

Список постів

Пости-дайджести, найновіші першими. Усі фільтри необов'язкові й комбінуються між собою.

ПараметрОбов'язковийОпис
countryніКод країни ISO-2
categoryніSlug категорії: politics, economy, war, society, sport, culture, world, tech, other
dateніКонкретний день, YYYY-MM-DD
fromніПочаток діапазону дат, YYYY-MM-DD (включно)
toніКінець діапазону дат, YYYY-MM-DD (включно)
qніПошук у заголовку й темі, до 100 символів
limitніКількість, 1–100; типово 20
offsetніЗсув для пагінації; типово 0
Приклад відповіді
{
  "data": [
    {
      "id": 24239,
      "country": "SI",
      "lang": "sl",
      "category": { "slug": "other", "label": "Drugo" },
      "trend": "češka korak bližje ukinitvi prispevka",
      "date": "2026-10-24",
      "title": "Tema dneva: Češka korak bližje ukinitvi prispevka",
      "digest": "»češka korak bližje ukinitvi prispevka« se danes pojavlja v več medijih (2). …",
      "source_count": 2,
      "published_at": "2026-10-24T14:05:09Z",
      "url": "https://trendnews.uk.app/post.php?id=24239"
    }
  ],
  "meta": { "total": 150, "limit": 1, "offset": 0, "has_more": true }
}
GET /api/v1/posts/{id} потрібен ключ

Один пост

Повні дані поста разом зі списком оригінальних джерел, на основі яких він зібраний.

ПараметрОбов'язковийОпис
idтакID поста (частина шляху)
Приклад відповіді
{
  "data": {
    "id": 24242,
    "country": "GB",
    "lang": "en",
    "category": { "slug": "sport", "label": "Sport" },
    "trend": "aston villa man city",
    "date": "2026-10-24",
    "title": "Story of the day: Aston villa man city",
    "digest": "One of the day's top stories is “aston villa man city”, reported by 3 sources. …",
    "source_count": 3,
    "published_at": "2026-10-24T14:05:10Z",
    "url": "https://trendnews.uk.app/post.php?id=24242",
    "sources": [
      { "title": "…", "url": "https://www.standard.co.uk/…", "host": "www.standard.co.uk", "sitename": "Evening Standard" }
    ]
  }
}

Приклади коду

Отримати 10 останніх економічних постів України:

curl -H "X-API-Key: $TN_KEY" \
  "https://trendnews.uk.app/api/v1/posts?country=UA&category=economy&limit=10"

Обійти всі сторінки результатів:

offset = 0
while True:
    page = requests.get(f"{BASE}/api/v1/posts", params={"country": "GB", "limit": 100, "offset": offset},
                        headers={"X-API-Key": KEY}, timeout=10).json()
    handle(page["data"])
    if not page["meta"]["has_more"]:
        break
    offset += 100

Спробувати

Ключ можна створити в кабінеті. Запит іде прямо з вашого браузера. Ключ ніде не зберігається й зникає після перезавантаження сторінки.