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.
To list existing feed groups and their configurations:
await client.feeds.listFeedGroups();
// List all feed groups (excluding soft-deleted ones)$response = $feedsClient->listFeedGroups(false);// List all feed groups including soft-deleted ones$response = $feedsClient->listFeedGroups(true);
var response = await _feedsV3Client.ListFeedGroupsAsync(false);
# 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 onesresponse = self.client.feeds.list_feed_groups(include_deleted=True)
# List all feed groups (excluding soft-deleted ones)response = client.feeds.list_feed_groups(false)# List all feed groups including soft-deleted onesresponse = client.feeds.list_feed_groups(true)
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.
const response = await serverClient.feeds.createFeedGroup({ id: "mytimeline", // Run the activity processors to analyse topics for text & images activity_processors: [ { type: "text_interest_tags" }, { type: "image_interest_tags" }, ], // Activity selectors change which activities are included in the feed // The default "following" selectors gets activities from the feeds you follow // The "popular" activity selectors includes the popular activities // And "interest" activities similar to activities you've engaged with in the past // You can use multiple selectors in 1 feed activity_selectors: [ { type: "popular", }, { type: "following", }, { type: "interest", }, ], // Rank for a user based on interest score // This calculates a score 0-1.0 of how well the activity matches the user's prior interest ranking: { type: "interest", score: "decay_linear(time) * interest_score * decay_linear(popularity)", },});const forYouFeed = client.feeds.feed("mytimeline", "thierry");const response = await forYouFeed.getOrCreate({ user_id: "thierry" });
ctx := context.Background()// Create feed group with activity processors, activity selectors, and rankingcreateFeedGroupRequest := &getstream.CreateFeedGroupRequest{ ID: "mytimeline", // Run the activity processors to analyse topics for text & images ActivityProcessors: []getstream.ActivityProcessorConfig{ {Type: "text_interest_tags"}, {Type: "image_interest_tags"}, }, // Activity selectors change which activities are included in the feed // The default "following" selectors gets activities from the feeds you follow // The "popular" activity selectors includes the popular activities // And "interest" activities similar to activities you've engaged with in the past // You can use multiple selectors in 1 feed ActivitySelectors: []getstream.ActivitySelectorConfig{ { Type: getstream.PtrTo("popular"), }, { Type: getstream.PtrTo("following"), }, { Type: getstream.PtrTo("interest"), }, }, // Rank for a user based on interest score // This calculates a score 0-1.0 of how well the activity matches the user's prior interest Ranking: &getstream.RankingConfig{ Type: getstream.PtrTo("interest"), Score: getstream.PtrTo("decay_linear(time) * interest_score * decay_linear(popularity)"), },}// Create the feed groupresponse, err := client.Feeds().CreateFeedGroup(ctx, createFeedGroupRequest)if err != nil { log.Fatal("Error creating feed group:", err)}fmt.Printf("Feed group created successfully!\n")fmt.Printf("Duration: %s\n", response.Data.Duration)fmt.Printf("Feed Group ID: %s\n", response.Data.FeedGroup.ID)// Create and get the feed for a specific userforYouFeed := client.Feeds().Feed("mytimeline", "thierry")feedResponse, err := forYouFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{ UserID: getstream.PtrTo("thierry"),})if err != nil { log.Fatal("Error getting or creating feed:", err)}fmt.Printf("Feed created/retrieved successfully!\n")fmt.Printf("Feed ID: %s\n", feedResponse.Data.Feed.ID)fmt.Printf("Feed Group: %s\n", feedResponse.Data.Feed.GroupID)
use GetStream\GeneratedModels\CreateFeedGroupRequest;use GetStream\GeneratedModels\ActivitySelectorConfig;use GetStream\GeneratedModels\RankingConfig;use GetStream\GeneratedModels\ActivityProcessorConfig;use GetStream\GeneratedModels\GetOrCreateFeedRequest;// Create feed group with activity processors, activity selectors, and ranking$createFeedGroupRequest = new CreateFeedGroupRequest( id: "mytimeline", // Run the activity processors to analyse topics for text & images activityProcessors: [ new ActivityProcessorConfig(type: "text_interest_tags"), new ActivityProcessorConfig(type: "image_interest_tags"), ], // Activity selectors change which activities are included in the feed // The default "following" selectors gets activities from the feeds you follow // The "popular" activity selectors includes the popular activities // And "interest" activities similar to activities you've engaged with in the past // You can use multiple selectors in 1 feed activitySelectors: [ new ActivitySelectorConfig(type: "popular"), new ActivitySelectorConfig(type: "following"), new ActivitySelectorConfig(type: "interest"), ], // Rank for a user based on interest score // This calculates a score 0-1.0 of how well the activity matches the user's prior interest ranking: new RankingConfig( type: "interest", score: "decay_linear(time) * interest_score * decay_linear(popularity)" ));// Create the feed group$response = $feedsClient->createFeedGroup($createFeedGroupRequest);// Create and get the feed for a specific user$forYouFeed = $feedsClient->feed("mytimeline", "thierry");$feedRequest = new GetOrCreateFeedRequest(userID: "thierry");$feedResponse = $forYouFeed->getOrCreateFeed($feedRequest);
require 'getstream_ruby'# Create feed group with activity processors, activity selectors, and rankingcreate_request = GetStream::Generated::Models::CreateFeedGroupRequest.new( id: "mytimeline", activity_processors: [ GetStream::Generated::Models::ActivityProcessorConfig.new(type: "text_interest_tags"), GetStream::Generated::Models::ActivityProcessorConfig.new(type: "image_interest_tags") ], activity_selectors: [ GetStream::Generated::Models::ActivitySelectorConfig.new(type: "popular"), GetStream::Generated::Models::ActivitySelectorConfig.new(type: "following"), GetStream::Generated::Models::ActivitySelectorConfig.new(type: "interest") ], ranking: GetStream::Generated::Models::RankingConfig.new( type: "interest", score: "decay_linear(time) * interest_score * decay_linear(popularity)" ))client.feeds.create_feed_group(create_request)# Create and get the feed for a specific userget_or_create_request = GetStream::Generated::Models::GetOrCreateFeedRequest.new( user_id: "thierry")feed_response = client.feeds.get_or_create_feed("mytimeline", "thierry", get_or_create_request)
import io.getstream.services.FeedsImpl;import io.getstream.models.*;FeedsImpl feedsClient = new FeedsImpl(new StreamHTTPClient("<API key>", "<API secret>"));CreateFeedGroupRequest createRequest = CreateFeedGroupRequest.builder() .id("mytimeline") .activityProcessors(List.of( ActivityProcessorConfig.builder().type("text_interest_tags").build(), ActivityProcessorConfig.builder().type("image_interest_tags").build() )) .activitySelectors(List.of( ActivitySelectorConfig.builder().type("popular").build(), ActivitySelectorConfig.builder().type("following").build(), ActivitySelectorConfig.builder().type("interest").build() )) .ranking(RankingConfig.builder() .type("interest") .score("decay_linear(time) * interest_score * decay_linear(popularity)") .build()) .build();CreateFeedGroupResponse response = feedsClient.createFeedGroup(createRequest).execute().getData();// Create and get the feed for a specific userFeed forYouFeed = new Feed("mytimeline", "thierry", feedsClient);GetOrCreateFeedRequest feedRequest = GetOrCreateFeedRequest.builder() .userID("thierry") .build();forYouFeed.getOrCreate(feedRequest);
var createRequest = new CreateFeedGroupRequest{ ID = "mytimeline", ActivityProcessors = new List<ActivityProcessorConfig> { new() { Type = "text_interest_tags" }, new() { Type = "image_interest_tags" } }, ActivitySelectors = new List<ActivitySelectorConfig> { new() { Type = "popular" }, new() { Type = "following" }, new() { Type = "interest" } }, Ranking = new RankingConfig { Type = "interest", Score = "decay_linear(time) * interest_score * decay_linear(popularity)" }};var response = await _feedsV3Client.CreateFeedGroupAsync(createRequest);// Create and get the feed for a specific userawait _feedsV3Client.GetOrCreateFeedAsync( "mytimeline", "thierry", new GetOrCreateFeedRequest { UserID = "thierry" });
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 userfeed = self.client.feeds.feed("mytimeline", "thierry")feed.get_or_create(user_id="thierry")
const myNotificationGrpup = await serverClient.feeds.createFeedGroup({ id: "myid", // Group by activity type and day aggregation: { format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}' }, // Enable notification tracking notification: { track_read: true, track_seen: true, },});
myNotificationGroup, err := client.Feeds().CreateFeedGroup(context.Background(), &getstream.CreateFeedGroupRequest{ ID: "myid2", // Group by activity type and day Aggregation: &getstream.AggregationConfig{ Format: getstream.PtrTo("{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}"), }, // Enable notification tracking Notification: &getstream.NotificationConfig{ TrackRead: getstream.PtrTo(true), TrackSeen: getstream.PtrTo(true), },})if err != nil { log.Fatal("Error creating feed group:", err)}
// Create notification feed group with aggregation and tracking$request = new GeneratedModels\CreateFeedGroupRequest( id: "myid", defaultVisibility: 'public', // Group by activity type and day aggregation: new GeneratedModels\AggregationConfig( format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}' ), // Enable notification tracking notification: new GeneratedModels\NotificationConfig( trackRead: true, trackSeen: true ));// Create the feed group$response = $feedsClient->createFeedGroup($request);
await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest{ ID = "aggregated-group", DefaultVisibility = "public", ActivityProcessors = new List<ActivityProcessorConfig> { new() { Type = "default" } }, Aggregation = new AggregationConfig { Format = "{{ type }}-{{ time.strftime(\"%Y-%m-%d\") }}" }});
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\") }}" ))
require 'getstream_ruby'my_notification_group = client.feeds.create_feed_group( GetStream::Generated::Models::CreateFeedGroupRequest.new( id: 'myid', # Group by activity type and day aggregation: GetStream::Generated::Models::AggregationConfig.new( format: '{{ type }}-{{ time.strftime("%Y-%m-%d") }}' ), # Enable notification tracking notification: GetStream::Generated::Models::NotificationConfig.new( track_read: true, track_seen: true ) ))
notificationFeed.markActivity( request = MarkActivityRequest( // Mark all notifications as read... markAllRead = true, // ...or only selected ones markRead = listOf( /* group names to mark 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 */ ],});
await notificationFeed.markActivity({ // Mark all notifications as read... mark_all_read: true, // ...or only selected ones mark_read: [ /* group names to mark 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 */ ],});
await notificationFeed.markActivity( request: const MarkActivityRequest( // Mark all notifications as read... markAllRead: true, // ...or only selected ones markRead: [ // group names to mark 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 */ ], user_id: "<user id>",});
notificationFeed := client.Feeds().Feed("notification", "john")_, err = notificationFeed.MarkActivity(context.Background(), &getstream.MarkActivityRequest{ // Mark all notifications as read... MarkAllRead: getstream.PtrTo(true), // ...or only selected ones MarkRead: []string{ // group names to mark as read }, UserID: getstream.PtrTo("john"),})if err != nil { log.Fatal("Error:", err)}
use GetStream\GeneratedModels\MarkActivityRequest;// Create notification feed$notificationFeed = $feedsClient->feed("notification", "john");// Mark all notifications as read$markRequest = new MarkActivityRequest( markAllRead: true, userID: "john");$response = $notificationFeed->markActivity($markRequest);// Or mark only selected notifications as read$markSelectedRequest = new MarkActivityRequest( markRead: [ // group names to mark as read ], userID: "john");$response = $notificationFeed->markActivity($markSelectedRequest);
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.
await serverClient.feeds.deleteFeedGroup({ id: "<feed group id to delete>",});
// Delete the feed group (soft delete by default)$response = $feedsClient->deleteFeedGroup("mytimeline", false);// For hard delete, pass true as the second parameter// $response = $feedsClient->deleteFeedGroup("mytimeline", true);
require 'getstream_ruby'# Soft delete (default)client.feeds.delete_feed_group("mytimeline", false)# Hard delete# client.feeds.delete_feed_group("mytimeline", true)