---
title: "Trending List"
method: GET
path: "/v1/trending"
---

# Trending List

`GET /v1/trending`

## Request

- Base URL: `https://api.storyflo.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.storyflo.com/v1/trending`
- Auth: none declared

Cross-vertical trending: recent published persona briefings grouped
by vertical + most-active source publishers in the last 24h + the
top individual articles ranked by ``trending_score`` + the most
recent daily-brief compilation as ``featured`` + the last 7 daily
briefs as ``recent_daily_briefs``.

Performance layout (post-2026-06-04):
  Q1 — briefings/by_vertical: still computed inline (cheap, ORDER BY +
       LIMIT over persona_briefings; sub-100ms).
  Q2 — active_publishers_24h: reads from
       ``trending_publisher_snapshot`` (migration 0135) which is
       UPSERTed every ~5 min by ``TrendingSnapshotWorker``. This
       replaces the GROUP BY over 20K+ ``is_quality=True`` articles
       that was running ~9.9s wall-clock on cold calls.
  Q3 — top_articles (NEW 2026-06-04; window widened 24h → 7d
       2026-06-04 + always-10 fallback): ORDER BY ``trending_score
       DESC NULLS LAST`` over the last 7 days of quality articles.
       Originally mirrored ``daily_brief_worker.py:825`` (24h
       window) but operator-flagged that the prior days' content
       must remain discoverable — especially during LLM-provider
       outages that strand most of the day's articles without a
       summary. A relaxed fallback pass fires when the strict list
       is shorter than 10, dropping the ``trending_score IS NOT
       NULL`` filter and ordering by ``created_at DESC`` to fill
       the gap, so the surface is always populated when any
       quality article exists in the window. Excludes
       ``slug LIKE 'daily-brief-%'`` in both passes so the
       compilation is featured separately and not double-billed.
  Q4 — featured (NEW 2026-06-04): the latest rendered
       ``daily-brief-YYYY-MM-DD`` article — the same article whose
       audio is published to the storyflo · trending Spotify show.
       Listeners now see it inside the in-app feed too.
  Q5 — recent_daily_briefs (NEW 2026-06-04): the last 7 rendered
       daily-brief compilations ordered by ``created_at DESC``.
       Acts as an always-populated anchor section for the trending
       UI even when ``top_articles`` is sparse — listeners can
       always scroll back to "yesterday's top 10". Only daily
       briefs with a done RenderJob (rendered audio available)
       appear; an unrendered brief is a dead row to a listener
       expecting playback to start on click.

The in-process 60s TTL cache (``app/trending_cache.py``) stays as a
second-line defence — cache key now includes ``top`` so different
callers share rows but don't collide.

Snapshot freshness contract: ``TrendingSnapshotWorker`` is the
primary driver (5-min loop); ``cron_extra_ticks.trending_snapshot_tick``
on the daily-brief loop is the safety-net. The worker fires its first
tick immediately on start so the snapshot row exists within seconds
of deploy. If the row is somehow missing, this endpoint returns
``[]`` for ``active_publishers_24h`` rather than falling back to the
9.9s aggregation.

## Query parameters

- `limit` integer
- `top` integer

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/storyflo/apis/storyflo-inference-router.md) · [All operations](https://skmtc.dev/storyflo/apis/storyflo-inference-router/llms.txt) · [OpenAPI document](https://skmtc.dev/storyflo/apis/storyflo-inference-router/revisions/c3a1a735ac7a?raw)
