request = {
"id": "myid",
"activity_selectors": [
{"type": "following"}
],
"ranking": {
"type": "recency"
},
"activity_processors": [
{"type": "text_interest_tags"}
],
"activity_filter": {
"exclude_owner_activities": True
},
"custom": {
"description": "My custom feed group"
}
}
response = self.client.feeds.create_feed_group(**request)Intro & Defaults
Feed groups are templates that define how different feeds behave in your application. The configuration options let you create feeds that work differently depending on your use case.
By adjusting settings like ranking, aggregation, activity selectors, and processors, you can tailor each feed group to serve specific purposes in your app.
Note that any write operation to feed groups/views can take up to 30 seconds to propagate to all API nodes.
Built-in groups
There are several feed groups setup by default.
| Group | Description |
|---|---|
user | A feed setup for the content a user creates. Typically you add activities here when someone writes a post |
timeline | The timeline feed is used when you're following. So if user Charlie is following John, timeline:charlie would follow user:john |
foryou | A version of the timeline feed that adds popular content, and prioritizes popularity over recency |
notification | A notification feed. Think of the bell icon you see in most apps |
story | A feed set up for users to post story activities (activities with expiration data) |
stories | A timeline feed which can be used to follow other users' stories. |
You can update the default feed group configurations or create your own feed groups.
Create feed groups
Here's how to create a feed group using the API:
Applications can't have more than 100 feed groups
Alternatively you can use the getOrCreateFeedGroup endpoint to create and apply settings, or return the existing group.
Exclude owner activities
Set activity_filter.exclude_owner_activities on a feed group to hide the owner's own activities when reading that owner's feed.
Each feed has a created_by_id. When this flag is on, the filter drops activities whose user_id matches the feed's created_by_id, so the feed owner's own posts are hidden from their own feed, but still visible to followers who see the fanout in their own feeds.
- Reading
user:jimmyhides activities whereuser_idisjimmy. - Reading
user:amystill shows activities fanned out fromuser:jimmy(and hides Amy's own).
The flag is set once at the feed group level (not per view) and applied automatically per feed. It works on the user, timeline, and foryou built-in groups, and on custom groups.
Where it's not valid: the API rejects activity_filter on:
notification,story, orstoriesbuilt-in groups- feed groups that also set
aggregation,notification, orstoriesconfig
For self-notifications, no configuration is needed. The Feeds API already skips creating notifications when the actor is the activity owner.
Update semantics: feed group updates use full-replacement semantics. Omitting activity_filter on an UpdateFeedGroup call clears the stored value, so include it on every update if you want to preserve it.
When flag changes take effect: changes apply on the next read. On ranked feeds (for example foryou, or groups with a ranking config), updating the feed group invalidates outstanding pagination tokens, so clients mid-pagination may need to restart from the first page.
Overview of the feed group model
FeedGroupResponse fields:
| Field | Description |
|---|---|
id | Feed group identifier. |
default_visibility | Default activity visibility (public, visible, followers, members, private). |
activity_selectors | Activity source selection rules. |
activity_processors | Enrichment and processing pipeline configuration. |
ranking | Ranking formula/configuration for feed ordering. |
aggregation | Aggregation settings for grouped reads. |
activity_filter | Read-time filters, including exclude_owner_activities. |
notification | Notification configuration for notification feeds. |
stories | Stories configuration for stories feeds. |
push_notification | Push notification behavior configuration. |
custom | Custom metadata object. |
created_at | Feed group creation timestamp. |
updated_at | Feed group last updated timestamp. |
deleted_at | Deletion timestamp (present when soft-deleted). |
List feed groups
To list existing feed groups and their configurations:
# List all feed groups (excluding soft-deleted ones)
response = self.client.feeds.list_feed_groups(include_deleted=False)
# List all feed groups including soft-deleted ones
response = self.client.feeds.list_feed_groups(include_deleted=True)Activity Ranking
When ranking activities you can specify a ranking formula.
self.client.feeds.create_feed_group(
id=feed_group_id,
default_visibility="public",
ranking=RankingConfig(
type="default",
score="decay_linear(time) * popularity"
)
)For you Feeds
Many apps want to have a "for you" or personalized feed. There are a couple benefits to a personalized feed:
- Works well even if your users don't spend much time setting up follows
- Can be a mechanism to discover new content or things to follow
Stream offers a few built-in methods to create a for you feed and gives you the API access to do more advanced customization if needed.
This next example is a bit more complicated. It uses an activity processor to add topic data to activities, activity selectors to pull in content from different sources, and ranks content you're likely to engage with higher in the feed.
create_request = {
"id": "mytimeline",
"activity_processors": [
{"type": "text_interest_tags"},
{"type": "image_interest_tags"}
],
"activity_selectors": [
{"type": "popular"},
{"type": "following"},
{"type": "interest"}
],
"ranking": {
"type": "interest",
"score": "decay_linear(time) * interest_score * decay_linear(popularity)"
}
}
response = self.client.feeds.create_feed_group(**create_request)
# Create and get the feed for a specific user
feed = self.client.feeds.feed("mytimeline", "thierry")
feed.get_or_create(user_id="thierry")Aggregation & Notification Feeds
Aggregation groups similar activities together, which is useful for notification feeds and reducing noise.
Aggregation Format
You can create your own aggregated feeds:
self.client.feeds.create_feed_group(
id = feed_group_id,
default_visibility="public",
activity_processors=[
ActivityProcessorConfig(type="default")
],
aggregation=AggregationConfig(
format="{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"
)
)Notification Feed Example
The built-in notification feed comes with the necessary configurations:
feed_response_1 = self.test_feed.get_or_create(user_id=self.test_user_id)
feed_response_2 = self.test_feed_2.get_or_create(
user_id=self.test_user_id_2
)Marking Notifications as Read
await notificationFeed.markActivity({
// Mark all notifications as read...
mark_all_read: true,
// ...or only selected ones
mark_read: [
/* group names to mark as read */
],
});Updating feed groups
It's possible to update any custom or built-in feed group:
response = self.client.feeds.update_feed_group(
"myid",
# Fields to update, e.g.:
# activity_processors=[...],
# activity_selectors=[...],
# ranking={"type": "recency"},
# custom={"description": "Updated feed group"},
)Deleting feed groups
Deleting feed groups will not cascade delete associated resources. It will make feeds within the feed group unreadable but any fanout that has happened before deletion will stay in effect.
If you need a clean slate during development it is advised to create a new feed group instead.
# Soft delete (default)
response = self.client.feeds.delete_feed_group("mytimeline", hard_delete=False)
# Hard delete
# response = self.client.feeds.delete_feed_group("mytimeline", hard_delete=True)