TrendNews API

REST API for reading digest posts and news trends across 110 countries. Format: JSON, UTF-8, all times in UTC.

Quick start

Base URL: https://trendnews.uk.app/api/v1. Sign up, create an API key in your account and send your first request:

curl -H "X-API-Key: YOUR_KEY" "https://trendnews.uk.app/api/v1/posts?country=UA&limit=5"

Countries are given as ISO-2 codes (UA, US, DE, PL…). The full list with feed languages is returned by /api/v1/countries. New posts appear every hour.

Authentication

Every request except /v1/health, must include the API key in a header:

X-API-Key: tn_live_…

Also accepted: Authorization: Bearer tn_live_…. Passing the key in URL parameters is not allowed: it would end up in logs and browser history.

Keep the key on your server. If you embed it in page JavaScript, any visitor can copy it. Route frontend requests through your backend.

Plans and limits

Limits depend on the account's plan. The daily quota is shared by all keys of the account and resets at 00:00 UTC. The per-minute limit applies to each key separately. All plans and prices

PlanoRequests per dayRequests per minuteAPI keys
Free1 000601
Pro50 0003005
Business500 0001 20020

The current state is returned in the headers of every response:

HeaderMeaning
X-Planthe account's plan: free, pro ou business
X-Quota-Limitdaily quota of the account
X-Quota-Remainingrequests left for today
X-Quota-Resetseconds until the quota resets (00:00 UTC)
X-RateLimit-Limitper-minute request limit of the key
X-RateLimit-Remainingrequests left in the current minute
Retry-Afteronly with a 429 response: seconds until you can retry

Responses may be cached for up to 60 seconds (Cache-Control: private, max-age=60): data is updated once an hour. Caching also saves your quota.

Response format and errors

A successful response always has the data; lists also have meta with pagination. An error has the error field with a machine-readable code and a message.

// success{ "data": [ … ], "meta": { "total": 856, "limit": 20, "offset": 0, "has_more": true } }

// error{
  "error": {
    "code": "invalid_country",
    "message": "Unknown country code. The list of supported ISO-2 codes: /api/v1/countries"
  }
}
HTTPcodeWhen
400missing_parama required parameter is missing
400invalid_param, invalid_country, invalid_datea parameter has an invalid value or format
401missing_api_keyrequest without a key
401invalid_api_keythe key is invalid or revoked
403account_blockedthe key owner's account is blocked
404not_foundunknown endpoint or the post does not exist
405method_not_alloweda method other than GET
429rate_limitedper-minute request limit exceeded
429quota_exceededthe plan's daily quota is used up (resets at 00:00 UTC)
500server_errorinternal server error

Pagination

Lists return at most limit records (up to 100). To get the next page, increase offset by limit, as long as meta.has_more is true.

Endpoints

GET /api/v1/health no key

Service status

Checks that the API is up and returns the time of the latest post. No key needed — handy for monitoring.

Example response
{
  "data": {
    "status": "ok",
    "last_post_at": "2026-10-24T14:05:09Z",
    "countries": 6,
    "time": "2026-10-24T14:31:38Z"
  }
}
GET /api/v1/countries key required

Countries and languages

All countries we collect news for: ISO-2 code, name, flag, feed language, number of posts and the date of the latest one.

Example response
{
  "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 key required

Country categories

Post categories of a country with the number of posts in each. Category names are in the country's language.

ParameterRequiredDescription
countryyesISO-2 country code (full list: /v1/countries)
Example response
{
  "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 key required

List posts

Digest posts, newest first. All filters are optional and can be combined.

ParameterRequiredDescription
countrynoISO-2 country code
categorynoCategory slug: politics, economy, war, society, sport, culture, world, tech, other
datenoA specific day, YYYY-MM-DD
fromnoStart of the date range, YYYY-MM-DD (inclusive)
tonoEnd of the date range, YYYY-MM-DD (inclusive)
qnoSearch in title and topic, up to 100 characters
limitnoNumber of items, 1–100; default 20
offsetnoPagination offset; default 0
Example response
{
  "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} key required

Single post

Full post data together with the list of original sources it was built from.

ParameterRequiredDescription
idyesPost ID (part of the path)
Example response
{
  "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" }
    ]
  }
}
GET /api/v1/events key required

Events

Events are groups of articles from different outlets about the same thing, with a stable event_id. Reprints, articles from one site and from one wire agency count as one independent source. The trend metric is transparent: mentions over 1/6/24 h, independent sources, velocity; the formula is in meta.trend_formula.

ParameterRequiredDescription
countrynoFeed country, ISO-2
regionnoEvent region: GeoNames code (IN.35) or name (Madhya Pradesh)
citynoEvent city: name (Satna) or GeoNames ID
categorynoCategory slug
langnoLanguage, ISO 639-1
sincenoEvents updated after this moment (UTC); default: the last 48 h
include_routineno1 — retornar também conteúdos de rotina (horóscopos, loterias, calendários de feriados, programação de TV, cotações diárias); ocultos por padrão, campo routine
min_independentnoMinimum independent sources; default 2
qnoSearch in the event title
sortnotrend (default), recent or sources
limitnoNumber of items, 1–100; default 20
offsetnoPagination offset; default 0
Example response
{
  "data": [
    {
      "id": "ev_k3v9q2m7x1a4d",
      "title": "Satna: Two killed as truck rams bike near Maihar",
      "country": "IN",
      "lang": "en",
      "category": { "slug": "society", "confidence": 0.67 },
      "first_seen": "2026-10-25T09:12:40Z",
      "original_published_at": "2026-10-25T08:41:00Z",
      "last_updated": "2026-10-25T13:05:12Z",
      "articles": 7,
      "sources_total": 6,
      "independent_sources": 4,
      "geo": {
        "source_country": "IN",
        "event": { "country": "IN", "region": "Madhya Pradesh", "region_code": "IN.35", "city": "Satna",
                   "city_geonames_id": 1257806, "lat": 24.6, "lon": 80.83, "confidence": 0.86 }
      },
      "trend": { "mentions_1h": 1, "mentions_6h": 5, "mentions_24h": 7, "independent_sources_24h": 4, "velocity": 2.5, "score": 18.6 },
      "evidence": { "available": true, "version": 2, "computed_at": "2026-10-25T13:20:03Z", "url": "https://trendnews.uk.app/api/v1/events/ev_k3v9q2m7x1a4d/evidence" }
    }
  ],
  "meta": { "total": 1, "limit": 20, "offset": 0, "has_more": false, "sort": "trend",
            "trend_formula": "score = independent_sources_24h × log2(1 + mentions_6h) × (1 + min(velocity, 3) / 3); …" }
}
GET /api/v1/events/{id} key required

Single event

The event with all its articles: publisher, publication date and its quality (from the feed, timezone-corrected, or from the page metadata), when we first saw the article, wire agency and independence group.

ParameterRequiredDescription
idyesEvent ID (part of the path)
Example response
{
  "data": {
    "id": "ev_k3v9q2m7x1a4d",
    "title": "Satna: Two killed as truck rams bike near Maihar",
    "independent_sources": 4,
    "…": "усі поля з /v1/events",
    "sources": [
      { "url": "https://…", "publisher": "Dainik Bhaskar", "title": "…", "lang": "hi",
        "published_at": "2026-10-25T08:41:00Z", "published_at_quality": "page_metadata",
        "first_seen": "2026-10-25T09:12:40Z", "agency": null, "independence_group": 1 },
      { "url": "https://…", "publisher": "…", "agency": "PTI", "independence_group": 2 },
      { "url": "https://…", "publisher": "…", "agency": "PTI", "independence_group": 2 }
    ]
  }
}
GET /api/v1/events/{id}/evidence key required

Event evidence

Fatos com links para as fontes, citações, contradições, incógnitas, mudanças desde a versão anterior e um tamanho de texto recomendado. Duas camadas: extrativa (todos os planos) e refinada pelo Claude (plano Business); veja a seção "Eventos e evidências" abaixo. A primeira solicitação coloca o evento na fila (202, tente novamente em ~30 s).

ParameterRequiredDescription
idyesEvent ID (part of the path)
sincenoReturn in what_changed every change after this moment (UTC)
layernoCamada de evidências: auto (padrão), refined (só Business) ou extractive
Example response
{
  "data": {
    "event_id": "ev_k3v9q2m7x1a4d",
    "version": 2,
    "computed_at": "2026-10-25T13:20:03Z",
    "independent_sources": 4,
    "sources_read": 6,
    "full_text_read": 5,
    "layer": "refined",
    "summary": "A speeding truck hit a motorcycle near Maihar in Satna district on Saturday morning, killing two people; police say the driver fled.",
    "facts": [
      { "claim": "Two people were killed when a speeding truck hit a motorcycle near Maihar in Satna district on Saturday morning.",
        "kind": "number", "status": "confirmed", "independent_sources": 3,
        "sources": [ { "url": "https://…", "publisher": "…", "published_at": "2026-10-25T08:41:00Z", "snippet": "…" } ] },
      { "claim": "Police said the truck driver fled the spot.", "kind": "statement", "status": "single_source", "independent_sources": 1, "sources": [ … ] }
    ],
    "quotes": [ { "text": "The driver fled after the accident, a search is under way", "speaker": "SP Ashutosh Gupta", "source": { "url": "https://…", "publisher": "…" } } ],
    "contradictions": [ { "topic": "Three people were injured…", "versions": [ { "claim": "Three people were injured…", "source": "https://…" }, { "claim": "Four people were injured…", "source": "https://…" } ] } ],
    "unknowns": [ { "statement": "The cause of the accident is not yet known.", "source": "https://…" } ],
    "what_changed": { "since_version": 1, "since": "2026-10-25T10:02:11Z",
                      "items": [ { "type": "new_fact", "claim": "Police said the truck driver fled the spot.", "status": "single_source" } ] },
    "suggested_length_words": 250,
    "method": "extractive + LLM: … confirmed = cited in >= 2 independent sources (computed, not by the model)",
    "llm": { "model": "claude-haiku-5-5", "input_tokens": 6050, "output_tokens": 1800 },
    "stale": false
  }
}

Eventos e evidências: como funciona

A camada de eventos agrupa os artigos de todos os feeds em eventos: uma notícia republicada por dezenas de veículos é um único evento com um único event_id. Abaixo explicamos como cada campo é calculado, para que você possa explicar os dados aos seus leitores.

Evento e event_id

Os artigos são agrupados por significado (embeddings multilíngues multilingual-e5) numa janela de 72 horas dentro do país do feed. Palavras-chave em comum e a geografia também são verificadas, para não juntar eventos diferentes sobre um tema parecido. O event_id (formato ev_ + 13 caracteres) não muda enquanto o evento é atualizado: novos artigos entram no evento existente em vez de criar um novo.

Independência das fontes

Os artigos partilham um independence_group quando vêm do mesmo site, citam a mesma agência (Reuters, AP, AFP, PTI, ANI, Ukrinform etc.) ou são cópias quase idênticas. sources_total conta todos os veículos, independent_sources conta os grupos independentes. Uma notícia de agência republicada por vinte sites conta como uma única fonte. Por padrão /v1/events retorna eventos com pelo menos 2 fontes independentes (parâmetro min_independent).

Data de publicação

original_published_at é a data de publicação confiável mais antiga entre as fontes, first_seen é quando vimos o evento pela primeira vez. Para cada artigo, published_at_quality indica a origem da data: feed — do feed; feed_timezone_corrected — o feed deu a hora local marcada como UTC e nós a corrigimos pelo fuso horário do país; page_metadata — dos metadados da página; unknown_used_first_seen — sem data, usa-se a hora da primeira detecção.

Geografia

geo.source_country é o país do feed onde o artigo foi publicado. geo.event é onde o evento aconteceu: país, região com código GeoNames (region_code, p. ex. IN.35 — Madhya Pradesh), cidade com city_geonames_id e coordenadas. Os locais são procurados em títulos e descrições no dicionário geográfico GeoNames (cidades com mais de 15 000 habitantes e regiões, incluindo formas locais). Filtros de /v1/events: region (código ou nome) e city (nome ou ID GeoNames). Se o local não for determinado com confiança, geo.event é null.

Idioma e categoria

O idioma de cada artigo é detectado pelo texto (fastText) e substitui o idioma do feed quando a confiança é de pelo menos 0,8. O idioma do evento é o da maioria dos seus artigos. A categoria é uma votação dos artigos do evento; category.confidence é a parcela de votos a favor dela.

Métricas de tendência

O campo trend é transparente: mentions_1h, mentions_6h, mentions_24h são artigos novos em 1, 6 e 24 horas, independent_sources_24h são grupos independentes num dia e velocity é o ritmo das últimas 6 horas em relação às 18 anteriores. A fórmula de score também é retornada em meta.trend_formula:

score = independent_sources_24h × log2(1 + mentions_6h) × (1 + min(velocity, 3) / 3)
velocity = (mentions_6h / 6) / ((mentions_24h − mentions_6h) / 18)

Evidências: duas camadas

/v1/events/{id}/evidence lê os textos completos de até 10 fontes (primeiro as independentes, respeitando o robots.txt) e retorna fatos com links, citações, contradições, incógnitas, what_changed e suggested_length_words. Este último é um tamanho de texto recomendado: depende do número de fatos confirmados, para que a matéria não seja inflada com enchimento.

Camada extrativa (todos os planos): um fato é uma frase literal de uma fonte, e frases com o mesmo significado são agrupadas. confirmed significa que a afirmação aparece em pelo menos duas fontes independentes, single_source em uma, disputed que as fontes dão números diferentes.

Camada refinada (plano Business): o Claude reescreve as frases das fontes como fatos atômicos e adiciona um breve summary neutro. O modelo não pode acrescentar nada por conta própria: cada fato deve citar frases das fontes, um fato sem citação é descartado, snippet é uma citação literal e o status confirmed é calculado pelo sistema a partir da independência das fontes citadas, não pelo modelo. Uma contradição só é registrada quando as fontes dão valores mutuamente exclusivos para o mesmo fato.

O parâmetro layer: auto (padrão: camada refinada para Business, extrativa para os demais planos), refined (só Business; caso contrário, 403 plan_required), extractive. O campo layer da resposta indica qual camada foi retornada. A primeira solicitação de um cliente Business coloca o refinamento na fila: enquanto ele é preparado (geralmente menos de 30 s), a camada extrativa é retornada com refined_status: processing e o cabeçalho Retry-After. No plano Pro é possível experimentar a camada refinada: layer=refined para 20 eventos diferentes por mês (o restante no cabeçalho X-Refined-Trial-Remaining). Para outros planos a resposta inclui refined_layer com um link para os planos. O campo llm indica o modelo e o número de tokens.

"Confirmado" significa que pelo menos duas fontes independentes o publicaram, não que a afirmação seja verdadeira. Em temas sensíveis, confira as fontes originais pelos links em sources.

Atualização e retenção

Os eventos são recalculados a cada hora junto com a coleta de notícias. As evidências são atualizadas automaticamente para eventos com 3 ou mais fontes independentes e para eventos cujo refinamento um cliente Business solicitou nas últimas 24 horas. stale: true significa que chegaram artigos novos após o último cálculo e a atualização já está na fila. Artigos, eventos e evidências são mantidos por 3 dias após a última atualização; posts e tendências, por mais tempo.

Webhooks (Business)

Em vez de consultar a API, receba um POST com JSON no seu endereço https. Configure no painel, aba “Webhooks”: endereço, países, mínimo de fontes independentes e tipos de eventos. event.created é enviado uma vez, quando um evento atinge o seu número de fontes independentes; evidence.updated, quando as evidências mudam (com what_changed; para Business, a camada refinada quando houver).

Cada solicitação é assinada: cabeçalho X-TrendNews-Signature: t=<hora unix>,v1=<hex>, onde v1 = HMAC-SHA256 de <t>.<corpo bruto> com o seu segredo. Verifique a assinatura e rejeite solicitações com mais de 5 minutos. Responda com 2xx em até 8 segundos; caso contrário, tentamos de novo após 1, 5, 30, 120 e 360 minutos. X-TrendNews-Delivery é um id único de entrega para evitar duplicatas.

Exemplo de corpo event.created:

{
  "type": "event.created",
  "created_at": "2026-10-25T18:04:11Z",
  "data": {
    "id": "ev_aacw4p3jhkye6", "title": "Storm Boelo brings 80mph gusts and travel disruption to Scotland",
    "country": "GB", "lang": "en", "category": "society",
    "first_seen": "2026-10-23T18:16:53Z", "original_published_at": "2026-10-23T18:16:53Z",
    "independent_sources": 17, "sources_total": 22,
    "geo": { "country": "GB", "region": "Scotland", "region_code": "GB.SCT", "city": null },
    "api_url": "https://trendnews.uk.app/api/v1/events/ev_aacw4p3jhkye6",
    "evidence_url": "https://trendnews.uk.app/api/v1/events/ev_aacw4p3jhkye6/evidence",
    "page_url": "https://trendnews.uk.app/event.php?id=ev_aacw4p3jhkye6"
  }
}

Verificação da assinatura (PHP):

$body = file_get_contents('php://input');
preg_match('/t=(\d+),v1=([0-9a-f]{64})/', $_SERVER['HTTP_X_TRENDNEWS_SIGNATURE'] ?? '', $m);
$ok = $m && abs(time() - (int)$m[1]) < 300
   && hash_equals(hash_hmac('sha256', $m[1] . '.' . $body, getenv('TRENDNEWS_WEBHOOK_SECRET')), $m[2]);
if (!$ok) { http_response_code(401); exit; }
$event = json_decode($body, true);   // handle $event['type'] … then
http_response_code(204);

Buscas salvas

Uma busca salva é uma consulta que executamos a cada hora sobre as notícias novas. Os resultados ficam guardados 30 dias e estão disponíveis no seu RSS privado (rss_url), pela API e — no Business — num webhook do tipo watch.match. Limites: Free — 1 busca, Pro — 10, Business — 100. Crie buscas na sua conta ou pela API.

Sintaxe: as palavras separadas por espaços devem aparecer todas (no título ou no lead); "frase exata"; OR para alternativas; -palavra para excluir. O modo events envia cada evento com resultado e pelo menos min_sources fontes independentes (2 ou mais); o modo articles, cada artigo novo correspondente.

# create
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"query": "\"green hydrogen\" OR electrolyser", "countries": ["DE","NL"], "mode": "events", "min_sources": 2}' \
  https://trendnews.uk.app/api/v1/watches

# list, one, delete
curl -H "X-API-Key: $KEY" https://trendnews.uk.app/api/v1/watches
curl -H "X-API-Key: $KEY" https://trendnews.uk.app/api/v1/watches/12
curl -X DELETE -H "X-API-Key: $KEY" https://trendnews.uk.app/api/v1/watches/12

# new hits since the last one you have
curl -H "X-API-Key: $KEY" "https://trendnews.uk.app/api/v1/watches/12/hits?since_id=3051&limit=100"

Exemplo de corpo watch.match:

{
  "type": "watch.match",
  "created_at": "2026-10-26T19:12:40Z",
  "data": {
    "watch": { "id": 12, "name": "Hydrogen", "query": "\"green hydrogen\" OR electrolyser" },
    "hit": { "kind": "event", "id": "ev_x8ox05csqqyae", "title": "…", "url": "https://trendnews.uk.app/event.php?id=ev_x8ox05csqqyae",
             "country": "DE", "lang": "de", "independent_sources": 3, "published_at": "2026-10-26T17:40:02Z",
             "api_url": "https://trendnews.uk.app/api/v1/events/ev_x8ox05csqqyae" }
  }
}

A primeira execução de uma busca nova preenche o RSS com os resultados do último dia sem enviá-los ao webhook; depois, ao webhook chega apenas o que é novo. Assinatura e novas tentativas funcionam como nos outros webhooks.

Mercados

Preços de Bitcoin, Ethereum, Solana, XRP, ouro, prata, petróleo Brent, S&P 500, Nasdaq, EUR/USD e USD/UAH junto com as notícias que os pressionam. O Claude marca cada evento com pelo menos duas fontes independentes: de qual ativo se trata, para onde pressiona o preço (up, down, neutral) e quão importante é (impact 1–3). Preços da Binance e do Yahoo Finance, atualizados a cada 15 minutos.

ConsultaRetorna
GET /v1/marketsTodos os ativos: preço, variação em 1 h / 24 h / 7 dias, news_24h — quantas notícias pressionam para cima e para baixo e o saldo score de −1 a 1, o último movimento brusco.
GET /v1/markets/{asset}Um ativo, suas notícias das últimas hours horas (48 por padrão) e os 5 últimos movimentos bruscos.
GET /v1/markets/{asset}/pricesCandles OHLC: res=1h (até 120 dias) ou res=1d (até 2 anos), parâmetro limit.
GET /v1/markets/{asset}/movesMovimentos bruscos: um candle de uma hora além do limite do ativo (de 0,5% para EUR/USD a 3% para Solana e XRP). O Claude escreve a explicação apenas com as manchetes em torno do movimento; lang — en, uk, es, pt, de, fr.
GET /v1/markets/{asset}/newsNotícias sobre o ativo com direção e peso; parâmetros hours e limit.

Ativos: bitcoin, ethereum, solana, xrp, gold, silver, brent, sp500, nasdaq, eurusd, usduah. Os mesmos dados estão na página https://trendnews.uk.app/markets/.

curl -H "X-API-Key: YOUR_KEY" "https://trendnews.uk.app/api/v1/markets/bitcoin/moves?lang=en&limit=5"

A relação entre as notícias e o preço é provável, não comprovada. Isto não é aconselhamento de investimento.

MCP para agentes de IA

O TrendNews tem um servidor MCP (Model Context Protocol): um agente de IA — Claude, Cursor ou o seu próprio — busca eventos, lê evidências com citações, consulta tendências e gerencia buscas salvas por conta própria. Endereço: https://trendnews.uk.app/mcp (Streamable HTTP). A autorização é a mesma chave de API no cabeçalho X-API-Key; cada chamada de ferramenta conta como uma requisição à API na cota do seu plano.

Ferramentas: search_events, get_event, get_event_evidence, get_trends, list_digests, list_countries, get_markets, explain_price_moves, list_saved_searches, create_saved_search, get_saved_search_hits.

# Claude Code
claude mcp add --transport http trendnews https://trendnews.uk.app/mcp --header "X-API-Key: YOUR_KEY"

# Cursor, Windsurf, Claude Desktop… (mcp.json)
{
  "mcpServers": {
    "trendnews": {
      "url": "https://trendnews.uk.app/mcp",
      "headers": { "X-API-Key": "YOUR_KEY" }
    }
  }
}

O servidor informa ao agente que «confirmado» significa «noticiado por pelo menos duas fontes independentes», não «verdadeiro», e pede que cite as fontes originais.

Code examples

Get the 10 latest economy posts for Ukraine:

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

Iterate over all result pages:

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

Try it

You can create a key in your account. The request goes straight from your browser. The key is not stored anywhere and is gone after a page reload.