Hot Feed Cache

Hot feed cache speeds up high-traffic feeds that many users read the same way. Stream computes the feed once, caches the result for a short window, and serves that shared ordering to all readers. Thousands of duplicate per-user reads become one cached read.

Hot feed cache is off by default. Contact support to request access.

When to use it

Use hot feed cache for feeds where all readers should see the same content and ordering:

  • Global or editorial timelines
  • Live event feeds
  • Leaderboards
  • Any ranked feed with identical results for every user

Do not use it for per-user personalized ordering. Feeds with interest ranking or other per-user rank signals are ineligible and fall back to a normal read.

How it works

  • Refresh window: The cache refreshes on an interval on the order of tens of seconds to about a minute. Content can be that stale between refreshes; decide whether that fits your use case (a leaderboard is usually fine; a sub-second live ticker might not).
  • Shared ordering: All readers hitting the same cache entry see the same activity order.
  • Frozen randomness and time: If your ranking expression uses randomness (rand_normal) or time-based values (current_time), the result is frozen per cache snapshot (shared across readers, re-computed each refresh) rather than evaluated per request.
  • Transparent fallback: If a request is not eligible for the cache, Stream serves a normal feed read with no error. (Long-idle pagination tokens can expire; see Pagination.) No client-side flag is required once Stream has enabled the feature for your feed.

Eligibility

Hot feed cache applies when all of the following are true:

  • Stream has enabled the feature for your app and feed
  • The read is made server-side (backend SDK or server token), not from a client SDK
  • The feed uses default (recency) or expression-based ranking, including external ranking
  • The read does not pass a request-level filter, unless Stream has enabled filter_tags caching for that feed (see below)

Reads that pass a request-level filter are not cached. The one exception is filter_tags, which can be cached only when Stream has enabled tag caching for that feed. Request it as part of your hot feed cache setup. Each distinct tag set is cached separately, up to a per-feed limit Stream configures.

Requests that aren't cached

When a request is not cacheable, Stream falls back to a normal read automatically. No error is returned.

ConditionBehavior
Client-side requestNormal read
Interest or personalized rankingNormal read
Interest weights in rankingNormal read
Ranking expressions using per-user state (is_read, is_seen)Normal read
Request includes a filter other than filter_tagsNormal read
filter_tags on a feed not opted in for tag cachingNormal read

Response differences when served from cache

When a request is served from cache, the response differs in a few ways:

  • own_reactions omitted: Cached pages are identical across users, so per-user own_reactions are not included. If your UI needs them, fetch them for the whole page in a single call with batch query activity reactions rather than one request per activity. To hydrate reactions on comment threads the same way, use batch query comment reactions.
  • No blocked-user filtering: Block lists are not applied to cached responses. Apply them in your backend before returning data to clients if required.
  • score_vars omitted on cached pages: If you set include_score_vars in enrichment options, cached pages omit score_vars; they're returned only on a freshly computed page. See Inspecting ranking variables.

Pagination

Paginated reads are served from an immutable snapshot of the ranked order, so every page of a given pagination stays consistent and a routine content refresh does not interrupt an in-progress pagination. The snapshot is kept alive while you're actively paging (each page you fetch extends it), so the pagination token expires only after a prolonged pause mid-pagination. If it does expire, the API returns 400 with pagination token expired, please refresh the feed. Restart from page 1.

Changing external_ranking mid-pagination also rejects the stale token with that error. Dropping external_ranking on a follow-up page falls off the cached path and is recomputed instead. See External ranking and Paginate efficiently for how ranked-feed cursors work in general.

External ranking

External ranking is supported. Ordering is shared per unique external_ranking parameter set, so different parameter values produce separate cache entries.

Performance: Keep external_ranking combinations bounded. When you use a ranking view with external_ranking, the ranked result is cached per distinct combination of external_ranking values. Reusing a bounded set of combinations (typically dozens to a few hundred) lets reads be served from cache and stay fast. Sending a unique external_ranking payload on every request defeats this cache and re-runs ranking on each read, which increases latency under high request rates. Prefer a stable, limited set of ranking-parameter combinations, and remember to resend the same external_ranking on paginated requests.

Resend external_ranking on every page. Keep the payload identical on every page of a paginated read. Change external_ranking mid-pagination and the API rejects the stale token with 400 (pagination token expired, please refresh the feed). Drop it entirely and the request falls off the cached path and is recomputed on the canonical read path. Either way you lose the cached snapshot for that page.

// Server-side call (backend SDK / server token)
$params = (object) [
    'popularity_multiplier' => 1,
    'share_multiplier' => 100,
];

$page1 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(
        userID: 'system',
        limit: 20,
        externalRanking: $params,
    )
);

// Page 2: pass the same externalRanking
$page2 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(
        userID: 'system',
        limit: 20,
        next: $page1->getData()->next,
        externalRanking: $params,
    )
);

Requesting access

Contact support with:

  • Your Stream app ID
  • The feed group and feed ID (or feed pattern) you want optimized
  • Whether you need filter_tags caching (and which tag sets you expect to read at high volume)

Stream enables hot feed cache per app and feed after reviewing your use case.