Ranking transparency (DSA)

The EU Digital Services Act (DSA) asks online platforms to explain in plain language how their recommender systems rank content (Article 27). Very large online platforms must also offer at least one feed option that is not based on profiling (Article 38).

Stream gives you the building blocks below. They support DSA compliance; they do not make your app compliant on their own, so do your own legal review.

Ranking disclosure

Publish a short, plain-language description of how your For You feed works. Start from this template and fill in each bracket from your feed group configuration:

**How we choose what you see in For You**

For You shows posts from [sources]. We order them by [main signals].

Your interests come from [interest source]. You can change what you see by choosing
"Show more" or "Show less" on a post, or by hiding it.

"Why am I seeing this" on each post shows the main reasons it was included.

You can switch to [non-personalized option], which does not use your activity.
Bracket Where it comes from
[sources] The feed group's activity selectors, for example following (people you follow), popular (popular posts), follow_suggestion (accounts we suggest), proximity (posts near you), interest (posts matching your interests).
[main signals] The variables in the feed group's ranking, for example how recent a post is, its reactions, comments, shares and bookmarks, whether you follow the author, and your interests.
[interest source] How user interests are set in your app: computed from what the user reacts to, picked at onboarding, or both.
[non-personalized option] The feed you serve as the non-personalized option (see below), for example "a chronological feed of people you follow".

"Show more", "Show less" and hiding a post are activity feedback. "Why am I seeing this" is include_reasons.

Non-personalized feed

Serve the non-personalized option from a feed with no ranking and no interest-based selectors, so it does not depend on the user's activity. Without a ranking, a feed lists activities newest first.

The built-in timeline group, which lists activities from feeds the user follows, works as long as you have not added a ranking or an interest selector to it. Otherwise create a dedicated feed group:

client.feeds.create_feed_group(
    id="chronological",
    activity_selectors=[{"type": "following"}],
)
Warning:

A feed view without a ranking keeps its feed group's ranking, so a view on the foryou group that only changes the selectors still uses the For You ranking.

Opting out of personalization

When a user opts out, your app stores their choice and reads the non-personalized feed for them in place of For You. There is no server-side opt-out setting.