# Feeds

## Creating a Feed

<admonition type="info">

When using server-side SDKs, the `user_id` parameter is required in the `getOrCreate` request. It's needed in order to automatically create a user. Client-side SDKs do not require this parameter as the user is authenticated via token.

</admonition>

<Tabs>

```swift label="Swift"
// Feed with no extra fields, of feed group "user"
let feed = client.feed(group: "user", id: "john")
try await feed.getOrCreate()

// More options
let query = FeedQuery(
    group: "user",
    id: "jack",
    data: .init(
        description: "My personal feed",
        name: "jack",
        visibility: "public",
        filterTags: ["tech", "hiking", "cooking"]
    )
)
let feed2 = client.feed(for: query)
try await feed.getOrCreate()
```

```kotlin label="Kotlin"
// Feed with no extra fields, of feed group "user"
val feed = client.feed(group = "user", id = "john")
feed.getOrCreate()

// More options
val query = FeedQuery(
    group = "user",
    id = "jack",
    data = FeedInputData(
        description = "My personal feed",
        name = "jack",
        visibility = FeedVisibility.Public,
        filterTags = listOf("tech", "hiking", "cooking")
    )
)
val feed2 = client.feed(query = query)
feed2.getOrCreate()
```

```js label="JavaScript"
// Feed with no extra fields, of feed group "user"
const feed = client.feed("user", "jack");
await feed.getOrCreate();
// Subscribe to WebSocket events for state updates
await feed.getOrCreate({ watch: true });

// More options
const feed = client.feed("user", "jack");
await feed.getOrCreate({
  data: {
    description: "My personal feed",
    name: "jack",
    visibility: "public",
    filter_tags: ["tech", "hiking", "cooking"],
  },
});
```

```js label="React"
// Feed with no extra fields, of feed group "user"
const feed = client.feed("user", "jack");
await feed.getOrCreate();
// Subscribe to WebSocket events for state updates
await feed.getOrCreate({ watch: true });

// More options
const feed = client.feed("user", "jack");
await feed.getOrCreate({
  data: {
    description: "My personal feed",
    name: "jack",
    visibility: "public",
    filter_tags: ["tech", "hiking", "cooking"],
  },
});
```

```js label="React Native"
// Feed with no extra fields, of feed group "user"
const feed = client.feed("user", "jack");
await feed.getOrCreate();
// Subscribe to WebSocket events for state updates
await feed.getOrCreate({ watch: true });

// More options
const feed = client.feed("user", "jack");
await feed.getOrCreate({
  data: {
    description: "My personal feed",
    name: "jack",
    visibility: "public",
    filter_tags: ["tech", "hiking", "cooking"],
  },
});
```

```dart label="Dart"
// Feed with no extra fields, of feed group "user"
final feed = client.feed(group: 'user', id: 'john');
await feed.getOrCreate();

// More options
const query = FeedQuery(
  fid: FeedId(group: 'user', id: 'jack'),
  data: FeedInputData(
    description: 'My personal feed',
    name: 'jack',
    visibility: FeedVisibility.public,
    filterTags: ['tech', 'hiking', 'cooking'],
  ),
);

final feed2 = client.feedFromQuery(query);
await feed2.getOrCreate();
```

```js label="Node"
// Feed with no extra fields, of feed group "user"
const feed = client.feeds.feed("user", "jack");
await feed.getOrCreate({
  // The owner of the feed
  user_id: "<user id>",
});

// More options
const feed = client.feeds.feed("user", "jack");
await feed.getOrCreate({
  data: {
    description: "My personal feed",
    name: "jack",
    visibility: "public",
    filter_tags: ["tech", "hiking", "cooking"],
  },
  // The owner of the feed
  user_id: "<user id>",
});
```

```go label="Go"
// Example 1
response, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})

if err != nil {
  log.Fatal("Error:", err)
}
log.Printf("Success: %+v\n", response)

// Example 2
response2, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
  Data: &getstream.FeedInput{
    Description: getstream.PtrTo("My personal feed"),
    Name:        getstream.PtrTo("John Hikes"),
    Visibility:  getstream.PtrTo("public"),
    FilterTags:  []string{"tech", "hiking", "cooking"},
  }})
if err != nil {
  log.Fatal("Error creating advanced feed:", err)
}
log.Printf("Success: %+v\n", response2)
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).filterTags(List.of("tech", "hiking", "cooking")).build();
GetOrCreateFeedRequest feedRequest2 =
    GetOrCreateFeedRequest.builder().userID(testUserId2).filterTags(List.of("tech", "hiking", "cooking")).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
```

```php label="php"
$feed = $feedsClient->feed('user', 'john');
$feedResponse = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: 'john')
);

// More options
$feed2 = $feedsClient->feed('user', 'jack');
$feedResponse2 = $feed2->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(
        userID: 'jack',
        data: new GeneratedModels\FeedInput(
            description: 'My personal feed',
            name: 'jack',
            visibility: 'public',
            filterTags: ['tech', 'hiking', 'cooking']
        )
    )
);
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId, FilterTags = new[] { "tech", "hiking", "cooking" } }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId2,
    request: new GetOrCreateFeedRequest { UserID = _testUserId2, FilterTags = new[] { "tech", "hiking", "cooking" } }
);
```

```python label="Python"
feed_response_1 = self.test_feed.get_or_create(
    user_id=self.test_user_id,
    filter_tags=["tech", "hiking", "cooking"]
)
feed_response_2 = self.test_feed_2.get_or_create(
    user_id=self.test_user_id_2,
    filter_tags=["tech", "hiking", "cooking"]
)
```

```ruby label="Ruby"
require 'getstream_ruby'

# Feed with no extra fields, of feed group "user"
feed = client.feed('user', 'john')
request = GetStream::Generated::Models::GetOrCreateFeedRequest.new(user_id: 'john')
feed_response = feed.get_or_create_feed(request)

# More options with custom data
feed2 = client.feed('user', 'jack')
request2 = GetStream::Generated::Models::GetOrCreateFeedRequest.new(
  user_id: 'jack',
  data: GetStream::Generated::Models::FeedInput.new(
    description: 'My personal feed',
    name: 'jack',
    visibility: 'public',
    filter_tags: ['tech', 'hiking', 'cooking']
  )
)
feed_response2 = feed2.get_or_create_feed(request2)
```

</Tabs>

## Built-in feed groups

| 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.                                                              |


## Reading a Feed

Here is a basic example of how to read a feed:

<Tabs>

```swift label="Swift"
let feed = client.feed(group: "user", id: "john")
try await feed.getOrCreate()
let feedData = feed.state.feed
let activities = feed.state.activities
let members = feed.state.members
```

```kotlin label="Kotlin"
val feed = client.feed(group = "user", id = "john")
feed.getOrCreate()
val feedData = feed.state.feed
val activities = feed.state.activities
val members = feed.state.members
```

```js label="JavaScript"
const feed = client.feed("user", "john");
await feed.getOrCreate({ watch: true });
const currentState = feed.state.getLatestValue();

const visivility = currentState.visibility;
const name = currentState.name;
const description = currentState.description;
const activities = currentState.activities;
const members = currentState.members;

// Or subscribe to state changes
const unsubscribe = feed.state.subscribe((state) => {
  // Called everytime the state changes
  console.log(state);
});

// or if you care only part of the state
const unsubscribe2 = feed.state.subscribeWithSelector(
  (state) => ({
    activities: state.activities,
  }),
  (state, prevState) => {
    console.log(state.activities, prevState?.activities);
  },
);

// Unsubscribe when you no longer want to recieve updates
unsubscribe();
unsubscribe2();
```

```js label="React"
import { useCallback } from "react";

const feed = client.feed("user", "john");
await feed.getOrCreate({ watch: true });

const selector = useCallback((state: FeedState) => ({
  name: state.name,
  description: state.description,
  members: state.members,
}), []);

const { activities } = useFeedActivities(feed);
const { name, description, members } = useStateStore(feed.state, selector);
```

```js label="React Native"
import { useCallback } from "react";

const feed = client.feed("user", "john");
await feed.getOrCreate({ watch: true });

const selector = useCallback((state: FeedState) => ({
  name: state.name,
  description: state.description,
  members: state.members,
}), []);

const { activities } = useFeedActivities(feed);
const { name, description, members } = useStateStore(feed.state, selector);
```

```dart label="Dart"
final feed = client.feed(group: 'user', id: 'john');
await feed.getOrCreate();
final feedData = feed.state.feed;
final activities = feed.state.activities;
final members = feed.state.members;

// Note: Always dispose the feed when you are done with it
feed.dispose();
```

```js label="Node"
const feed = client.feed(group: "user", id: "john")
const response = await feed.getOrCreate({
    user_id: '<user id>'
})

const feedData = response.feed;
const activities = response.activities;
const members = response.members;
```

```go label="Go"
feed := client.Feeds().Feed("user", "john")
response, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error calling getOrCreate:", err)
}

feedData := response.Data.Feed
activities := response.Data.Activities
members := response.Data.Members
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();

testFeedId = feedResponse1.getFeed().getFeed();
```

```php label="php"
$feed = $feedsClient->feed('user', 'john');

$response = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: 'john')
);

// Access feed data
$feedData = $response->getData()->feed;
$activities = $response->getData()->activities;
$members = $response->getData()->members;
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
```

```python label="Python"
feed_response_1 = self.test_feed.get_or_create(user_id=self.test_user_id)
```

```ruby label="Ruby"
require 'getstream_ruby'

# Reading a feed
feed = client.feed('user', 'john')
request = GetStream::Generated::Models::GetOrCreateFeedRequest.new(user_id: 'john')
feed_response = feed.get_or_create_feed(request)

# Access feed data
feed_data = feed_response.feed
activities = feed_response.activities
members = feed_response.members
```

</Tabs>

The response will contain the following data.

You have more options when reading a feed, let's go over a few:

<Tabs>

```swift label="Swift"
let query = FeedQuery(
    group: "user",
    id: "john",
    activityFilter: .in(.filterTags, ["green"]), // filter activities with filter tag green
    activityLimit: 10,
    externalRanking: ["user_score": 0.8], // additional data used for ranking
    followerLimit: 10,
    followingLimit: 10,
    memberLimit: 10,
    view: "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
    watch: true // receive web-socket events with real-time updates
)
let feed = client.feed(for: query)
try await feed.getOrCreate()
let activities = feed.state.activities
let feedData = feed.state.feed
```

```kotlin label="Kotlin"
val query = FeedQuery(
    group = "user",
    id = "john",
    activityFilter = ActivitiesFilterField.filterTags.`in`("green"), // filter activities with filter tag green
    activityLimit = 10,
    externalRanking = mapOf("user_score" to 0.8), // additional data used for ranking
    followerLimit = 10,
    followingLimit = 10,
    memberLimit = 10,
    view = "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
    watch = true // receive web-socket events with real-time updates
)
val feed = client.feed(query = query)
feed.getOrCreate()
val activities = feed.state.activities
val feedData = feed.state.feed
```

```js label="JavaScript"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  limit: 10,
  filter: {
    filter_tags: ["green"], // filter activities with filter tag green
  },
  external_ranking: {
    user_score: 0.8, // additional data used for ranking
  },
  followers_pagination: {
    limit: 10,
  },
  following_pagination: {
    limit: 10,
  },
  member_pagination: {
    limit: 10,
  },
  view: "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
});
```

```js label="React"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  limit: 10,
  filter: {
    filter_tags: ["green"], // filter activities with filter tag green
  },
  external_ranking: {
    user_score: 0.8, // additional data used for ranking
  },
  followers_pagination: {
    limit: 10,
  },
  following_pagination: {
    limit: 10,
  },
  member_pagination: {
    limit: 10,
  },
  view: "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
});
```

```js label="React Native"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  limit: 10,
  filter: {
    filter_tags: ["green"], // filter activities with filter tag green
  },
  external_ranking: {
    user_score: 0.8, // additional data used for ranking
  },
  followers_pagination: {
    limit: 10,
  },
  following_pagination: {
    limit: 10,
  },
  member_pagination: {
    limit: 10,
  },
  view: "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
});
```

```dart label="Dart"
const query = FeedQuery(
  fid: FeedId(group: 'user', id: 'john'),
  // filter activities with filter tag green
  activityFilter: Filter.in_(
    ActivitiesFilterField.filterTags,
    ['green'],
  ),
  activityLimit: 10,
  // additional data used for ranking
  externalRanking: {'user_score': 0.8},
  followerLimit: 10,
  followingLimit: 10,
  memberLimit: 10,
  // overwrite the default ranking or aggregation logic for this feed. good for split testing
  view: 'myview',
  // receive web-socket events with real-time updates
  watch: true,
);

final feed = client.feedFromQuery(query);
await feed.getOrCreate();
final activities = feed.state.activities;
final feedData = feed.state.feed;

// Note: Always dispose the feed when you are done with it
feed.dispose();
```

```js label="Node"
const feed = client.feeds.feed("user", "jack");
const response = await feed.getOrCreate({
  limit: 10,
  filter: {
    filter_tags: ["green"], // filter activities with filter tag green
  },
  external_ranking: {
    user_score: 0.8, // additional data used for ranking
  },
  followers_pagination: {
    limit: 10,
  },
  following_pagination: {
    limit: 10,
  },
  member_pagination: {
    limit: 10,
  },
  view: "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
  user_id: "<user id>",
});
```

```go label="Go"
feed := client.Feeds().Feed("user", "jack")
request := &getstream.GetOrCreateFeedRequest{
  Limit:  intPtr(10),
  UserID: stringPtr("<user id>"),
  View:   stringPtr("myview"),
  Filter: map[string]any{
    "filter_tags": []string{"green"}, // filter activities with filter tag green
  },
  ExternalRanking: map[string]any{
    "user_score": 0.8, // additional data used for ranking
  },
  FollowersPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
  FollowingPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
  MemberPagination: &getstream.PagerRequest{
    Limit: intPtr(10),
  },
}
response, err := feed.GetOrCreate(context.Background(), request)
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).build();
GetOrCreateFeedRequest feedRequest2 =
    GetOrCreateFeedRequest.builder().userID(testUserId2).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
```

```php label="php"
$feed = $feedsClient->feed('user', 'jack');

$response = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(
        limit: 10,
        filter: (object)['filter_tags' => ['green']],
        externalRanking: (object)['user_score' => 0.8],
        followersPagination: new GeneratedModels\PagerRequest(limit: 10),
        followingPagination: new GeneratedModels\PagerRequest(limit: 10),
        memberPagination: new GeneratedModels\PagerRequest(limit: 10),
        view: 'myview',
        userID: 'jack'
    )
);
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId2,
    request: new GetOrCreateFeedRequest { UserID = _testUserId2 }
);
```

```python label="Python"
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
)
```

```ruby label="Ruby"
require 'getstream_ruby'

# Reading multiple feeds
feed1 = client.feed('user', 'john')
request1 = GetStream::Generated::Models::GetOrCreateFeedRequest.new(user_id: 'john')
feed_response_1 = feed1.get_or_create_feed(request1)

feed2 = client.feed('user', 'jane')
request2 = GetStream::Generated::Models::GetOrCreateFeedRequest.new(user_id: 'jane')
feed_response_2 = feed2.get_or_create_feed(request2)
```

</Tabs>

## Enrichment Options

You can control how activities are enriched when reading a feed by providing `enrichment_options` to the `getOrCreate` request. This allows you to customize which fields and related data are included in the response.

<open-api-models modelname="EnrichmentOptions" recursive="false" headerlevel="3">

</open-api-models>

<admonition type="info">

**Performance Optimization**: Enrichment options allow you to skip specific enrichments to improve performance.

**Important Notes**:

- **Reactions**: If `skip_activity_reactions` is set to `true`, the expensive enrichment step is skipped. However, if reactions are already denormalized in the database, they will still be included in the response. This option only skips the expensive enrichment step but does not remove already-loaded data for performance reasons.

- **Followers/Following**: If `skip_followers` or `skip_following` is set to `true`, fetching and enriching followers or following is skipped. However, if `followers_pagination` or `following_pagination` is explicitly provided in the request, followers or following will be fetched regardless of this setting. This ensures that explicit pagination requests are always honored.

</admonition>

### Example

<Tabs>

```js label="JavaScript"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  enrichment_options: {
    // Configure enrichment options here
  },
});
```

```js label="React"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  enrichment_options: {
    // Configure enrichment options here
  },
});
```

```js label="React Native"
const feed = client.feed("user", "jack");
const response = await feed.getOrCreate({
  enrichment_options: {
    // Configure enrichment options here
  },
});
```

```js label="Node"
const feed = client.feeds.feed("user", "jack");
const response = await feed.getOrCreate({
  enrichment_options: {
    // Configure enrichment options here
  },
  user_id: "<user id>",
});
```

```go label="Go"
feed := client.Feeds().Feed("user", "jack")
request := &getstream.GetOrCreateFeedRequest{
  UserID: stringPtr("<user id>"),
  EnrichmentOptions: &getstream.EnrichmentOptions{
    // Configure enrichment options here
  },
}
response, err := feed.GetOrCreate(context.Background(), request)
```

```python label="Python"
feed_response = feeds.get_or_create_feed(
    feed_group="user",
    feed_id="jack",
    enrichment_options={
        # Configure enrichment options here
    },
    user_id="jack"
)
```

```ruby label="Ruby"
feed = client.feed('user', 'jack')
response = feed.get_or_create_feed(
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: 'jack',
    enrichment_options: GetStream::Generated::Models::EnrichmentOptions.new(
      # Configure enrichment options here
    )
  )
)
```

</Tabs>

## Feed Pagination

Here is how you can read the next page on the feed:

<Tabs>

```swift label="Swift"
let feed = client.feed(
    for: .init(
        group: "user",
        id: "john",
        activityLimit: 10
    )
)
// Page 1
try await feed.getOrCreate()
let activities = feed.state.activities // First 10 activities

// Page 2
let page2Activities = try await feed.queryMoreActivities(limit: 10)

let page1And2Activities = feed.state.activities
```

```kotlin label="Kotlin"
val feed = client.feed(
    query = FeedQuery(
        group = "user",
        id = "john",
        activityLimit = 10
    )
)
// Page 1
feed.getOrCreate()
val activities = feed.state.activities // The flow emits the first 10 activities

// Page 2
val page2Activities: Result<List<ActivityData>> = feed.queryMoreActivities(limit = 10)

val page1And2Activities = feed.state.activities
```

```js label="JavaScript"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

// Second page
await feed.getNextPage();

console.log(feed.state.getLatestValue().is_loading_activities);
// Truthy if feed has next page
console.log(feed.state.getLatestValue().next);
console.log(feed.state.getLatestValue().activities);
// Only if feed group has aggregation turned on
console.log(feed.state.getLatestValue().aggregated_activities);
```

```js label="React"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

const { activities, loadNextPage, is_loading, has_next_page } =
  useFeedActivities(feed) ?? {};
// Only if feed group has aggregation turned on
const { aggregated_activities, is_loading, has_next_page } =
  useAggregatedActivities(feed) ?? {};
```

```js label="React Native"
const feed = client.feed("user", "jack");

// First page
await feed.getOrCreate({
  limit: 10,
});

const { activities, loadNextPage, is_loading, has_next_page } =
  useFeedActivities(feed) ?? {};
// Only if feed group has aggregation turned on
const { aggregated_activities, is_loading, has_next_page } =
  useAggregatedActivities(feed) ?? {};
```

```dart label="Dart"
final feed = client.feedFromQuery(
  const FeedQuery(
    fid: FeedId(group: 'user', id: 'john'),
    activityLimit: 10,
  ),
);

// Page 1
await feed.getOrCreate();
final activities = feed.state.activities; // First 10 activities

// Page 2
final page2Activities = await feed.queryMoreActivities(limit: 10);

final page1And2Activities = feed.state.activities;
```

```js label="Node"
const feed = client.feeds.feed("user", "jack");
const firstPage = await feed.getOrCreate({
  limit: 10,
  user_id: "user_id",
});

const nextPage = await feed.getOrCreate({
  next: firstPage.next,
  limit: 10,
  user_id: "user_id",
});
```

```go label="Go"
feed := client.Feeds().Feed("user", "john")

// First page
firstPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting first page:", err)
}

// Second page request using next cursor
nextPage, err := feed.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  Next:   firstPage.Data.Next,
  Limit:  getstream.PtrTo(10),
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting next page:", err)
}

log.Printf("First page activities count: %d", len(firstPage.Data.Activities))
log.Printf("Next page activities count: %d", len(nextPage.Data.Activities))
```

```java label="Java"
testFeed = new Feed("user", testUserId, feeds);
testFeed2 = new Feed("user", testUserId2, feeds);

GetOrCreateFeedRequest feedRequest1 =
    GetOrCreateFeedRequest.builder().userID(testUserId).build();
GetOrCreateFeedRequest feedRequest2 =
    GetOrCreateFeedRequest.builder().userID(testUserId2).build();

GetOrCreateFeedResponse feedResponse1 = testFeed.getOrCreate(feedRequest1).getData();
GetOrCreateFeedResponse feedResponse2 = testFeed2.getOrCreate(feedRequest2).getData();
testFeedId = feedResponse1.getFeed().getFeed();
testFeedId2 = feedResponse2.getFeed().getFeed();
```

```php label="php"
$feed = $feedsClient->feed('user', 'jack');

$feedResponse1 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: "jack", limit: 10)
);

$feedResponse2 = $feed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: "jack", limit: 10, next: $feedResponse1->getData()->next)
);
```

```csharp label="C#"
var feedResponse1 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId,
    request: new GetOrCreateFeedRequest { UserID = _testUserId }
);
var feedResponse2 = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: _testFeedId2,
    request: new GetOrCreateFeedRequest { UserID = _testUserId2 }
);
```

```python label="Python"
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
)
```

```ruby label="Ruby"
feed = client.feed('user', 'jack')

# First page
first_page = feed.get_or_create_feed(
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: 'jack',
    limit: 10
  )
)

# Second page using next cursor from first page
if first_page.next
  second_page = feed.get_or_create_feed(
    GetStream::Generated::Models::GetOrCreateFeedRequest.new(
      user_id: 'jack',
      limit: 10,
      next: first_page.next
    )
  )
end
```

</Tabs>


## Filters

`filter` provides a performant way to read a feed, and only select activities that match a given filter.

<admonition type="info">

Please note that filtering is typically used for fields with fixed value sets (for example `filter_tags`), for text based search, you should check out [query activities endpoint](/activity-feeds/docs/dotnet-csharp/query-activities/)

</admonition>

### Examples

<Tabs>

```swift label="Swift"
// Add a few activities
let feedId = FeedId(group: "user", id: "john")
try await client.upsertActivities([
    ActivityRequest(feeds: [feedId.rawValue], filterTags: ["green", "blue"], text: "first", type: "post"),
    ActivityRequest(feeds: [feedId.rawValue], filterTags: ["yellow", "blue"], text: "second", type: "post"),
    ActivityRequest(feeds: [feedId.rawValue], filterTags: ["orange"], text: "third", type: "post")
])
// Now read the feed, this will fetch activity 1 and 2
let query = FeedQuery(feed: feedId, activityFilter: .in(.filterTags, ["blue"]))
let feed = client.feed(for: query)
try await feed.getOrCreate()
let activities = feed.state.activities // contains first and second
```

```kotlin label="Kotlin"
// Add a few activities
val fid = FeedId(group = "user", id = "john")
client.upsertActivities(
    listOf(
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("green", "blue"), text = "first", type = "post"),
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("yellow", "blue"), text = "second", type = "post"),
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("orange"), text = "third", type = "post")
    )
)
// Now read the feed, this will fetch activity 1 and 2
val query = FeedQuery(fid = fid, activityFilter = ActivitiesFilterField.filterTags.`in`("blue"))
val feed = client.feed(query = query)
feed.getOrCreate()
val activities = feed.state.activities // contains first and second
```

```js label="JavaScript"
const feed = client.feed("user", "123");

// Add a few activities
client.upsertActivities({
  activities: [
    {
      feeds: [feed.feed],
      type: "post",
      text: "first",
      filter_tags: ["green", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "second",
      filter_tags: ["yellow", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "third",
      filter_tags: ["orange"],
    },
  ],
});

const response = await feed.getOrCreate({
  watch: true,
  filter: {
    filter_tags: ["blue"],
  },
});
```

```js label="React"
const feed = client.feed("user", "123");

// Add a few activities
client.upsertActivities({
  activities: [
    {
      feeds: [feed.feed],
      type: "post",
      text: "first",
      filter_tags: ["green", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "second",
      filter_tags: ["yellow", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "third",
      filter_tags: ["orange"],
    },
  ],
});

const response = await feed.getOrCreate({
  watch: true,
  filter: {
    filter_tags: ["blue"],
  },
});
```

```js label="React Native"
const feed = client.feed("user", "123");

// Add a few activities
client.upsertActivities({
  activities: [
    {
      feeds: [feed.feed],
      type: "post",
      text: "first",
      filter_tags: ["green", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "second",
      filter_tags: ["yellow", "blue"],
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "third",
      filter_tags: ["orange"],
    },
  ],
});

const response = await feed.getOrCreate({
  watch: true,
  filter: {
    filter_tags: ["blue"],
  },
});
```

```dart label="Dart"
// Add a few activities
const feedId = FeedId(group: 'user', id: 'john');
await client.upsertActivities(
  activities: [
    ActivityRequest(
      feeds: [feedId.rawValue],
      filterTags: const ['green', 'blue'],
      text: 'first',
      type: 'post',
    ),
    ActivityRequest(
      feeds: [feedId.rawValue],
      filterTags: const ['yellow', 'blue'],
      text: 'second',
      type: 'post',
    ),
    ActivityRequest(
      feeds: [feedId.rawValue],
      filterTags: const ['orange'],
      text: 'third',
      type: 'post',
    ),
  ],
);
// Now read the feed, this will fetch activity 1 and 2
const query = FeedQuery(
  fid: feedId,
  activityFilter: Filter.in_(ActivitiesFilterField.filterTags, ['blue']),
);

final feed = client.feedFromQuery(query);
await feed.getOrCreate();
// contains first and second
final activities = feed.state.activities;
```

```js label="Node"
const feed = client.feeds.feed("user", "123");
// Add a few activities
client.feeds.upsertActivities({
  activities: [
    {
      feeds: [feed.feed],
      type: "post",
      text: "first",
      filter_tags: ["green", "blue"],
      user_id: "<user id>",
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "second",
      filter_tags: ["yellow", "blue"],
      user_id: "<user id>",
    },
    {
      feeds: [feed.feed],
      type: "post",
      text: "third",
      filter_tags: ["orange"],
      user_id: "<user id>",
    },
  ],
});

const response = await feed.getOrCreate({
  filter: {
    filter_tags: ["blue"],
  },
  user_id: "<user id>",
});
```

```go label="Go"
ctx := context.Background()
// Create feed
feed := client.Feeds().Feed("user", "john")

// Create activities
activities := []getstream.ActivityRequest{
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("first"),
		FilterTags: []string{"green", "blue"},
		UserID:     stringPtr("john"),
	},
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("second"),
		FilterTags: []string{"yellow", "blue"},
		UserID:     stringPtr("john"),
	},
	{
		Feeds:      []string{"user:john"},
		Type:       "post",
		Text:       stringPtr("third"),
		FilterTags: []string{"orange"},
		UserID:     stringPtr("john"),
	},
}

// Upsert activities
_, err = client.Feeds().UpsertActivities(ctx, &getstream.UpsertActivitiesRequest{
	Activities: activities,
})
if err != nil {
	log.Fatal("Error upserting activities:", err)
}

// Get or create feed with filter
response, err := feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
	Filter: map[string]any{
		"filter_tags": []string{"blue"},
	},
	UserID: stringPtr("john"),
})
if err != nil {
	log.Fatal("Error getting or creating feed:", err)
}

log.Printf("Feed response: %+v", len(response.Data.Activities))
```

```java label="Java"
// Add a few activities
List<ActivityRequest> activities = List.of(
    ActivityRequest.builder()
        .feeds(List.of("user:123"))
        .type("post")
        .text("first")
        .filterTags(List.of("green", "blue"))
        .userID("123")
        .build(),
    ActivityRequest.builder()
        .feeds(List.of("user:123"))
        .type("post")
        .text("second")
        .filterTags(List.of("yellow", "blue"))
        .userID("123")
        .build(),
    ActivityRequest.builder()
        .feeds(List.of("user:123"))
        .type("post")
        .text("third")
        .filterTags(List.of("orange"))
        .userID("123")
        .build()
);

feeds.upsertActivities(
    UpsertActivitiesRequest.builder()
        .activities(activities)
        .build()
).execute();

// Now read the feed, this will fetch activity 1 and 2
Map<String, Object> filter = new HashMap<>();
filter.put("filter_tags", List.of("blue"));

GetOrCreateFeedRequest feedRequest = GetOrCreateFeedRequest.builder()
    .filter(filter)
    .userID("123")
    .build();

GetOrCreateFeedResponse response = feeds.getOrCreateFeed("user", "123", feedRequest).execute().getData();
```

```php label="php"
$feed = $feedsClient->feed('user', '123');

// Add a few activities
$feedsClient->upsertActivities(
    new GeneratedModels\UpsertActivitiesRequest(
        activities: [
            [
                'feeds' => [$feed->getFeedIdentifier()],
                'type' => 'post',
                'text' => 'first',
                'filter_tags' => ['green', 'blue'],
                'user_id' => '123',
            ],
            [
                'feeds' => [$feed->getFeedIdentifier()],
                'type' => 'post',
                'text' => 'second',
                'filter_tags' => ['yellow', 'blue'],
                'user_id' => '123',
            ],
            [
                'feeds' => [$feed->getFeedIdentifier()],
                'type' => 'post',
                'text' => 'third',
                'filter_tags' => ['orange'],
                'user_id' => '123',
            ],
        ]
    )
);

// Now read the feed, this will fetch activity 1 and 2
$response = $feedsClient->getOrCreateFeed(
    'user',
    '123',
    new GeneratedModels\GetOrCreateFeedRequest(
        filter: (object)['filter_tags' => ['blue']],
        userID: '123'
    )
);
```

```csharp label="C#"
// Add a few activities
await _feedsV3Client.UpsertActivitiesAsync(
    request: new UpsertActivitiesRequest
    {
        Activities = new List<ActivityRequest>
        {
            new ActivityRequest
            {
                Feeds = new List<string> { "user:123" },
                Type = "post",
                Text = "first",
                FilterTags = new List<string> { "green", "blue" },
                UserID = "123"
            },
            new ActivityRequest
            {
                Feeds = new List<string> { "user:123" },
                Type = "post",
                Text = "second",
                FilterTags = new List<string> { "yellow", "blue" },
                UserID = "123"
            },
            new ActivityRequest
            {
                Feeds = new List<string> { "user:123" },
                Type = "post",
                Text = "third",
                FilterTags = new List<string> { "orange" },
                UserID = "123"
            }
        }
    }
);

// Now read the feed, this will fetch activity 1 and 2
var response = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: "123",
    request: new GetOrCreateFeedRequest
    {
        Filter = new { filter_tags = new[] { "blue" } },
        UserID = "123"
    }
);
```

```python label="Python"
# Add a few activities
feeds.upsert_activities(
    activities=[
        {
            "feeds": ["user:123"],
            "type": "post",
            "text": "first",
            "filter_tags": ["green", "blue"],
            "user_id": "123"
        },
        {
            "feeds": ["user:123"],
            "type": "post",
            "text": "second",
            "filter_tags": ["yellow", "blue"],
            "user_id": "123"
        },
        {
            "feeds": ["user:123"],
            "type": "post",
            "text": "third",
            "filter_tags": ["orange"],
            "user_id": "123"
        }
    ]
)

# Now read the feed, this will fetch activity 1 and 2
response = feeds.get_or_create_feed(
    feed_group="user",
    feed_id="123",
    filter={"filter_tags": ["blue"]},
    user_id="123"
)
```

```ruby label="Ruby"
# Add a few activities
client.feeds.upsert_activities(
  GetStream::Generated::Models::UpsertActivitiesRequest.new(
    activities: [
      {
        "feeds" => ["user:123"],
        "type" => "post",
        "text" => "first",
        "filter_tags" => ["green", "blue"],
        "user_id" => "123"
      },
      {
        "feeds" => ["user:123"],
        "type" => "post",
        "text" => "second",
        "filter_tags" => ["yellow", "blue"],
        "user_id" => "123"
      },
      {
        "feeds" => ["user:123"],
        "type" => "post",
        "text" => "third",
        "filter_tags" => ["orange"],
        "user_id" => "123"
      }
    ]
  )
)

# Now read the feed, this will fetch activity 1 and 2
response = client.feeds.get_or_create_feed(
  "user",
  "123",
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    filter: { "filter_tags" => ["blue"] },
    user_id: "123"
  )
)
```

</Tabs>

The filter syntax also supports `$or` and `$and`, so here's an example that's a little more complicated:

<Tabs>

```swift label="Swift"
// Get all the activities where filter tags contain both "green" and "orange"
let query = FeedQuery(
    group: "user",
    id: "john",
    activityFilter: .and([
        .in(.filterTags, ["green"]),
        .in(.filterTags, ["orange"])
    ])
)
try await feed.getOrCreate()
let activities = feed.state.activities
```

```kotlin label="Kotlin"
// Get all the activities where filter tags contain both "green" and "orange"
val query = FeedQuery(
    group = "user",
    id = "john",
    activityFilter = Filters.and(
        ActivitiesFilterField.filterTags.`in`("green"),
        ActivitiesFilterField.filterTags.`in`("orange"),
    )
)
feed.getOrCreate()
val activities = feed.state.activities
```

```js label="JavaScript"
// Get all the activities where filter tags contain both "green" and "orange"
const response = await feed.getOrCreate({
  filter: {
    $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
  },
});
```

```js label="React"
// Get all the activities where filter tags contain both "green" and "orange"
const response = await feed.getOrCreate({
  filter: {
    $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
  },
});
```

```js label="React Native"
// Get all the activities where filter tags contain both "green" and "orange"
const response = await feed.getOrCreate({
  filter: {
    $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
  },
});
```

```dart label="Dart"
// Get all the activities where filter tags contain both "green" and "orange"
const query = FeedQuery(
  fid: FeedId(group: 'user', id: 'john'),
  activityFilter: Filter.and([
    Filter.in_(ActivitiesFilterField.filterTags, ['green']),
    Filter.in_(ActivitiesFilterField.filterTags, ['orange']),
  ]),
);

final feed = client.feedFromQuery(query);
await feed.getOrCreate();
final activities = feed.state.activities;
```

```js label="Node"
// Get all the activities where filter tags contain both "green" and "orange"
const response = await feed.getOrCreate({
  filter: {
    $and: [
      { filter_tags: ["green"] }
      { filter_tags: ["orange"] }
    ],
  },
  user_id: "<user id>",
});
```

```go label="Go"
// Get all the activities where filter tags contain both "green" and "orange"
feed := client.Feeds().Feed("user", "john")
response, err := feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
    Filter: map[string]any{
        "$and": []map[string]any{
            {"filter_tags": []string{"green"}},
            {"filter_tags": []string{"orange"}},
        },
    },
    UserID: getstream.PtrTo("john"),
})
if err != nil {
    log.Fatal("Error getting feed with filter:", err)
}
log.Printf("Activities: %+v", response.Data.Activities)
```

```java label="Java"
// Get all the activities where filter tags contain both "green" and "orange"
Map<String, Object> filter = new HashMap<>();
List<Map<String, Object>> andConditions = List.of(
    Map.of("filter_tags", List.of("green")),
    Map.of("filter_tags", List.of("orange"))
);
filter.put("$and", andConditions);

GetOrCreateFeedRequest request = GetOrCreateFeedRequest.builder()
    .filter(filter)
    .userID("john")
    .build();

GetOrCreateFeedResponse response = feeds.getOrCreateFeed("user", "john", request).execute().getData();
```

```php label="php"
$response = $feedsClient->getOrCreateFeed(
    'user',
    '123',
    new GeneratedModels\GetOrCreateFeedRequest(
        filter: (object)['and' => [
            'filter_tags' => ['green'],
            'filter_tags' => ['orange'],
        ]],
        userID: '123'
    )
);
```

```csharp label="C#"
// Get all the activities where filter tags contain both "green" and "orange"
var response = await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: "john",
    request: new GetOrCreateFeedRequest
    {
        Filter = new
        {
            and = new[]
            {
                new { filter_tags = new[] { "green" } },
                new { filter_tags = new[] { "orange" } }
            }
        },
        UserID = "john"
    }
);
```

```python label="Python"
# Get all the activities where filter tags contain both "green" and "orange"
response = feeds.get_or_create_feed(
    feed_group="user",
    feed_id="john",
    filter={
        "$and": [
            {"filter_tags": ["green"]},
            {"filter_tags": ["orange"]}
        ]
    },
    user_id="john"
)
```

```ruby label="Ruby"
# Get all the activities where filter tags contain both "green" and "orange"
response = client.feeds.get_or_create_feed(
  "user",
  "john",
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    filter: {
      "$and" => [
        { "filter_tags" => ["green"] },
        { "filter_tags" => ["orange"] }
      ]
    },
    user_id: "john"
  )
)
```

</Tabs>

### Supported filters

What filters you can use when reading a feed depends on the feed group (or view, if provided) configuration.

The [activity selectors page](/activity-feeds/docs/dotnet-csharp/activity-selectors/) explains this in detail, but some quick examples: the `user` group uses `current` selector, and the `timeline` group the `following` selector by default.

The following `filter` options are available for the `following` selector:

| name          | type                      | description            | supported operations      | example                                                  |
| ------------- | ------------------------- | ---------------------- | ------------------------- | -------------------------------------------------------- |
| `id`          | string or list of strings | The ID of the activity | `$in`, `$eq`              | `{ id: { $in: [ 'abc', 'xyz' ] } }`                      |
| `filter_tags` | list of strings           | Tags for filtering     | `$eq`, `$contains`, `$in` | `{ filter_tags: { $in: [ 'categoryA', 'categoryB' ] } }` |

The following `filter` options are available for the `current`, `popular`, `interest`, `proximity` and `query` selectors:

| name            | type                                              | description                                                      | supported operations                       | example                                                                                      |
| --------------- | ------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------- |
| `id`            | string or list of strings                         | The ID of the activity                                           | `$in`, `$eq`                               | `{ id: { $in: [ 'abc', 'xyz' ] } }`                                                          |
| `activity_type` | string or list of strings                         | The type of the activity                                         | `$in`, `$eq`                               | `{ activity_type: { $in: [ 'abc', 'xyz' ] } }`                                               |
| `user_id`       | string or list of strings                         | The ID of the user who created the activity                      | `$in`, `$eq`                               | `{ user_id: { $in: [ 'abc', 'xyz' ] } }`                                                     |
| `text`          | string                                            | The text content of the activity                                 | `$eq`, `$q`, `$autocomplete`               | `{ text: { $q: 'popularity' } }`                                                             |
| `search_data`   | object                                            | The extra metadata for search indexing                           | `$contains`, `$path_exists`                | `{ search_data: { $contains: { 'category': 'sports', 'status': 'active' } } }`               |
| `interest_tags` | list of strings                                   | Tags for user interests                                          | `$eq`, `$contains`                         | `{ interest_tags: { $in: [ 'sports', 'music' ] } }`                                          |
| `filter_tags`   | list of strings                                   | Tags for filtering                                               | `$eq`, `$contains`, `$in`                  | `{ filter_tags: { $in: [ 'categoryA', 'categoryB' ] } }`                                     |
| `created_at`    | string, must be formatted as an RFC3339 timestamp | The time the activity was created                                | `$eq`, `$gt`, `$lt`, `$gte`, `$lte`        | `{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }`                                        |
| `popularity`    | number                                            | The popularity score of the activity                             | `$eq`, `$ne`, `$gt`, `$lt`, `$gte`, `$lte` | `{ popularity: { $gte: 70 } }`                                                               |
| `near`          | object                                            | GEO point and a distance (in km) to search for activities within | `$eq`                                      | `{ near: { $eq: { lat: 40.0, lng: -74.0, distance: 200 } } }`                                |
| `within_bounds` | object                                            | GEO bounds to search for activities within                       | `$eq`                                      | `{ within_bounds: { $eq: { ne_lat: 40.0, ne_lng: -115.0, sw_lat: 32.0, sw_lng: -125.0 } } }` |

When filtering by `filter_tags`, a plain array (or `$eq`) uses **AND-logic**: the activity must contain **all** of the specified tags. Use `$in` if you want **OR-logic**, where the activity must contain **any** of the specified tags.

The filter syntax also supports `$or` and `$and`:

<Tabs>

```swift label="Swift"
// Get all the activities where filter tags contain both "green" and "orange"
let filter = .and([
  .in(.filterTags, ["green"]),
  .in(.filterTags, ["orange"])
])
```

```kotlin label="Kotlin"
// Get all the activities where filter tags contain both "green" and "orange"
val filter = Filters.and(
  ActivitiesFilterField.filterTags.`in`("green"),
  ActivitiesFilterField.filterTags.`in`("orange"),
)
```

```js label="JavaScript"
// Get all the activities where filter tags contain both "green" and "orange"
const filter = {
  $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
};
```

```js label="React"
// Get all the activities where filter tags contain both "green" and "orange"
const filter = {
  $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
};
```

```js label="React Native"
// Get all the activities where filter tags contain both "green" and "orange"
const filter = {
  $and: [{ filter_tags: ["green"] }, { filter_tags: ["orange"] }],
};
```

```dart label="Dart"
// Get all the activities where filter tags contain both "green" and "orange"
const filter = Filter.and([
  Filter.in_(ActivitiesFilterField.filterTags, ['green']),
  Filter.in_(ActivitiesFilterField.filterTags, ['orange']),
])
```

```js label="Node"
// Get all the activities where filter tags contain both "green" and "orange"
const filter = {
  $and: [
    { filter_tags: ["green"] }
    { filter_tags: ["orange"] }
  ],
}
```

```go label="Go"
// Get all the activities where filter tags contain both "green" and "orange"
filter := map[string]any{
  "$and": []map[string]any{
    {"filter_tags": []string{"green"}},
    {"filter_tags": []string{"orange"}},
  },
}
```

```java label="Java"
// Get all the activities where filter tags contain both "green" and "orange"
Map<String, Object> filter = new HashMap<>();
List<Map<String, Object>> andConditions = List.of(
    Map.of("filter_tags", List.of("green")),
    Map.of("filter_tags", List.of("orange"))
);
filter.put("$and", andConditions);
```

```php label="php"
$filter = (object)['and' => [
  'filter_tags' => ['green'],
  'filter_tags' => ['orange'],
]]
```

```csharp label="C#"
// Get all the activities where filter tags contain both "green" and "orange"
var filter = = new
{
  and = new[]
  {
    new { filter_tags = new[] { "green" } },
    new { filter_tags = new[] { "orange" } }
  }
}
```

```python label="Python"
# Get all the activities where filter tags contain both "green" and "orange"
filter = {
  "$and": [
    {"filter_tags": ["green"]},
    {"filter_tags": ["orange"]}
  ]
}
```

```ruby label="Ruby"
# Get all the activities where filter tags contain both "green" and "orange"
filter = {
  "$and" => [
    { "filter_tags" => ["green"] },
    { "filter_tags" => ["orange"] }
  ]
}
```

</Tabs>


When providing `filter` to read a feed, activity selector filters on group/view level are ignored.

## Overview of built-in fields

<open-api-models modelname="GetOrCreateFeedResponse" recursive="false" headerlevel="3">
</open-api-models>

<open-api-models modelname="FeedResponse" recursive="false" headerlevel="3">
</open-api-models>

## Updating a Feed

The `updateFeed` endpoint performs a partial update: only the fields you include in the request are changed, and each of those fields is completely overwritten.

<Tabs>

```swift label="Swift"
try await feed.update(
    name: "Updated feed name",
    filterTags: ["tech", "hiking", "cooking"],
    custom: ["color": "blue"]
)
```

```kotlin label="Kotlin"
feed.updateFeed(
    request = UpdateFeedRequest(
        name = "Updated feed name",
        filterTags = listOf("tech", "hiking", "cooking"),
        custom = mapOf("color" to "blue")
    )
)
```

```js label="JavaScript"
await feed.update({
  name: "Updated feed name",
  filter_tags: ["tech", "hiking", "cooking"],
  custom: {
    color: "blue",
  },
});
```

```js label="React"
await feed.update({
  name: "Updated feed name",
  filter_tags: ["tech", "hiking", "cooking"],
  custom: {
    color: "blue",
  },
});
```

```js label="React Native"
await feed.update({
  name: "Updated feed name",
  filter_tags: ["tech", "hiking", "cooking"],
  custom: {
    color: "blue",
  },
});
```

```dart label="Dart"
await feed.update(
  name: 'Updated feed name',
  filterTags: ['tech', 'hiking', 'cooking'],
  custom: {'color': 'blue'},
);
```

```js label="Node"
const response = await feed.update({
  created_by_id: "josh",
  name: "Updated feed name",
  filter_tags: ["tech", "hiking", "cooking"],
  custom: {
    color: "blue",
  },
});
```

```go label="Go"
response, err := feed.Update(context.Background(), &getstream.UpdateFeedRequest{
  CreatedByID: getstream.PtrTo("josh"),
  Name:        getstream.PtrTo("Updated feed name"),
  FilterTags:  []string{"tech", "hiking", "cooking"},
  Custom:      map[string]any{"color": "blue"},
})
if err != nil {
  log.Fatal("Error updating feed:", err)
}
```

```java label="Java"
UpdateFeedRequest request = UpdateFeedRequest.builder()
    .createdByID("josh")
    .name("Updated feed name")
    .filterTags(List.of("tech", "hiking", "cooking"))
    .custom(Map.of("color", "blue"))
    .build();

FeedResponse response = feed.update(request).getData();
```

```php label="php"
$response = $feed->updateFeed(
    new GeneratedModels\UpdateFeedRequest(
        createdByID: 'josh',
        name: 'Updated feed name',
        filterTags: ['tech', 'hiking', 'cooking'],
        custom: (object)['color' => 'blue']
    )
);
```

```csharp label="C#"
var response = await feed.UpdateAsync(new UpdateFeedRequest
{
    CreatedByID = "josh",
    Name = "Updated feed name",
    FilterTags = new[] { "tech", "hiking", "cooking" },
    Custom = new Dictionary<string, object> { ["color"] = "blue" }
});
```

```python label="Python"
response = feed.update(
    created_by_id='josh',
    name='Updated feed name',
    filter_tags=['tech', 'hiking', 'cooking'],
    custom={'color': 'blue'}
)
```

```ruby label="Ruby"
response = feed.update_feed(
  GetStream::Generated::Models::UpdateFeedRequest.new(
    created_by_id: 'josh',
    name: 'Updated feed name',
    filter_tags: ['tech', 'hiking', 'cooking'],
    custom: { 'color' => 'blue' }
  )
)
```

</Tabs>

### Clearing a feed's location

Since `updateFeed` is a partial update, omitting `location` from the request leaves it unchanged. To explicitly remove a feed's location, set `clear_location` to `true`:

<Tabs>

```js label="JavaScript"
await feed.update({
  clear_location: true,
});
```

```js label="Node"
const response = await feed.update({
  clear_location: true,
});
```

```go label="Go"
response, err := feed.Update(context.Background(), &getstream.UpdateFeedRequest{
  ClearLocation: getstream.PtrTo(true),
})
```

```python label="Python"
response = feed.update(
    clear_location=True
)
```

```php label="php"
$response = $feed->updateFeed(
    new GeneratedModels\UpdateFeedRequest(
        clearLocation: true
    )
);
```

```ruby label="Ruby"
response = feed.update_feed(
  GetStream::Generated::Models::UpdateFeedRequest.new(
    clear_location: true
  )
)
```

</Tabs>

<admonition type="warning">

`clear_location` and `location` cannot be used together in the same request. The API will return an error if both are provided.

</admonition>

### Changing visibility

Changing visibility has dedicated behavior and SDK examples, including how `pending_follows_action` works when loosening from `followers`.

See [Changing Feed Visibility](/activity-feeds/docs/dotnet-csharp/changing-feed-visibility/) for details.

## Deleting a Feed

<admonition type="danger">

Deleting a feed is an irreversible operation. This goes for **both soft and hard deletes**.

</admonition>

Deleting a feed will cascade delete the following entities:

- Follows - any follow relationship this feed is part of will be deleted
- Feed members
- Pinned activities
- Activity marks (read/seen)
- Activities - the following will be deleted for each activity
  - Reactions
  - Bookmarks
  - Comments - the following will be deleted for each comment
    - Reactions

The deletion of a feed will be done asynchronously. If the endpoint is called server side a `task_id` is returned and you
can use this task ID to track the deletion process.

The difference between a hard and soft delete is that soft deleting will soft delete the entities that supports this.
These are `feeds`, `activities` and `comments`. Soft deleted `activities` will retain their `reactions` and `bookmarks` until hard deleted. Soft deleted `comments` will retain their `reactions` until hard deleted.
This means that this data can still be exported with the [export endpoint](/activity-feeds/docs/dotnet-csharp/gdpr/#the-right-to-access-data).

<Tabs>

```swift label="Swift"
// Soft delete a feed
try await feed.delete(hardDelete: false)

// Hard delete a feed
try await feed.delete(hardDelete: true)
```

```kotlin label="Kotlin"
// Soft delete a feed
feed.deleteFeed(hardDelete = false)

// Hard delete a feed
feed.deleteFeed(hardDelete = true)
```

```js label="JavaScript"
// Soft delete a feed
await feed.delete({ hard_delete: false });

// Hard delete a feed
await feed.delete({ hard_delete: true });
```

```js label="React"
// Soft delete a feed
await feed.delete({ hard_delete: false });

// Hard delete a feed
await feed.delete({ hard_delete: true });
```

```js label="React Native"
// Soft delete a feed
await feed.delete({ hard_delete: false });

// Hard delete a feed
await feed.delete({ hard_delete: true });
```

```dart label="Dart"
// Soft delete a feed
await feed.delete(hardDelete: false);

// Hard delete a feed
await feed.delete(hardDelete: true);
```

```js label="Node"
// Soft delete a feed
const response = await feed.delete({ hard_delete: false });

// Hard delete a feed
const response = await feed.delete({ hard_delete: true });

// Check task progress for server side requests (you need to poll getTask)
const taskResponse = await client.getTask({ id: response.task_id });
console.log(taskResponse.status === "completed");
```

```go label="Go"
// Soft delete a feed
response, err := feed.Delete(context.Background(), &getstream.DeleteFeedRequest{
    HardDelete: getstream.PtrTo(false),
})

// Hard delete a feed
response, err := feed.Delete(context.Background(), &getstream.DeleteFeedRequest{
    HardDelete: getstream.PtrTo(true),
})

// Check task progress (you need to poll GetTask)
taskResponse, err := client.GetTask(context.Background(), response.Data.TaskID, &getstream.GetTaskRequest{})
fmt.Println(taskResponse.Data.Status == "completed")
```

```java label="Java"
// Soft delete a feed
DeleteFeedRequest deleteRequest = DeleteFeedRequest.builder()
    .hardDelete(false)
    .build();
DeleteFeedResponse response = feed.delete(deleteRequest).getData();

// Hard delete a feed
DeleteFeedRequest hardDeleteRequest = DeleteFeedRequest.builder()
    .hardDelete(true)
    .build();
feed.delete(hardDeleteRequest);

// Check task progress (you need to poll getTask)
GetTaskResponse taskResponse = client.getTask(response.getTaskId()).getData();
System.out.println(taskResponse.getStatus().equals("completed"));
```

```php label="php"
// Soft delete a feed
$response = $feed->delete(
    new GeneratedModels\DeleteFeedRequest(hardDelete: false)
);

// Hard delete a feed
$response = $feed->delete(
    new GeneratedModels\DeleteFeedRequest(hardDelete: true)
);

// Check task progress (you need to poll getTask)
$taskResponse = $client->getTask($response->getData()->taskID);
echo $taskResponse->getData()->status === 'completed';
```

```csharp label="C#"
// Soft delete a feed
var response = await feed.DeleteAsync(new DeleteFeedRequest { HardDelete = false });

// Hard delete a feed
await feed.DeleteAsync(new DeleteFeedRequest { HardDelete = true });

// Check task progress (you need to poll GetTaskAsync)
var taskResponse = await client.GetTaskAsync(response.TaskId);
Console.WriteLine(taskResponse.Status == "completed");
```

```python label="Python"
# Soft delete a feed
response = feed.delete(hard_delete=False)

# Hard delete a feed
response = feed.delete(hard_delete=True)

# Check task progress (you need to poll get_task)
task_response = client.get_task(response.task_id)
print(task_response.status == "completed")
```

```ruby label="Ruby"
require 'getstream_ruby'

# Soft delete a feed
response = feed.delete(hard_delete: false)

# Hard delete a feed
response = feed.delete(hard_delete: true)

# Check task progress (you need to poll get_task)
task_response = client.get_task(response.task_id)
puts task_response.status == 'completed'
```

</Tabs>

## Batch create and delete

You can create and delete multiple feeds in a single request using server-side SDKs. Both batch endpoints support a maximum of 100 feeds per request.

<Tabs>

```js label="Node"
const feeds = new Array(100).fill(0).map((_, index) => ({
  feed_id: `feed_${index}`,
  feed_group_id: "user",
  name: `Feed ${index}`,
  visibility: "public",
}));
const response = await serverClient.feeds.createFeedsBatch({
  feeds,
});
await serverClient.feeds.deleteFeedsBatch({
  feeds: response.feeds.map((feed) => feed.feed),
  hard_delete: true,
});
```

```go label="Go"
feeds := make([]getstream.FeedRequest, 100)
for i := 0; i < 100; i++ {
  feeds[i] = getstream.FeedRequest{
    FeedID:      fmt.Sprintf("feed_%d", i),
    FeedGroupID: "user",
    Name:        getstream.PtrTo(fmt.Sprintf("Feed %d", i)),
    Visibility:  getstream.PtrTo("public"),
  }
}
response, err := client.Feeds().CreateFeedsBatch(ctx, &getstream.CreateFeedsBatchRequest{Feeds: feeds})
if err != nil {
  log.Fatal(err)
}
feedIDs := make([]string, len(response.Data.Feeds))
for i, f := range response.Data.Feeds {
  feedIDs[i] = f.Feed
}
_, err = client.Feeds().DeleteFeedsBatch(ctx, &getstream.DeleteFeedsBatchRequest{
  Feeds:     feedIDs,
  HardDelete: getstream.PtrTo(true),
})
if err != nil {
  log.Fatal(err)
}
```

```java label="Java"
List<FeedRequest> feedRequests = IntStream.range(0, 100)
    .mapToObj(i -> FeedRequest.builder()
        .feedId("feed_" + i)
        .feedGroupId("user")
        .name("Feed " + i)
        .visibility("public")
        .build())
    .toList();
CreateFeedsBatchResponse createResponse = client.feeds().createFeedsBatch(
    CreateFeedsBatchRequest.builder().feeds(feedRequests).build()
).getData();
List<String> feedIds = createResponse.getFeeds().stream()
    .map(FeedResponse::getFeed)
    .toList();
client.feeds().deleteFeedsBatch(DeleteFeedsBatchRequest.builder()
    .feeds(feedIds)
    .hardDelete(true)
    .build());
```

```php label="php"
$feeds = array_map(fn($i) => new GeneratedModels\FeedRequest(
    feedId: "feed_$i",
    feedGroupId: 'user',
    name: "Feed $i",
    visibility: 'public',
), range(0, 99));
$response = $client->feeds()->createFeedsBatch(
    new GeneratedModels\CreateFeedsBatchRequest(feeds: $feeds)
);
$feedIds = array_map(
    fn($f) => $f->getFeed(),
    $response->getData()->getFeeds()
);
$client->feeds()->deleteFeedsBatch(
    new GeneratedModels\DeleteFeedsBatchRequest(
        feeds: $feedIds,
        hardDelete: true
    )
);
```

```csharp label="C#"
var feeds = Enumerable.Range(0, 100).Select(i => new FeedRequest
{
  FeedID = $"feed_{i}",
  FeedGroupID = "user",
  Name = $"Feed {i}",
  Visibility = "public",
}).ToList();
var createResponse = await client.Feeds.CreateFeedsBatchAsync(new CreateFeedsBatchRequest { Feeds = feeds });
var feedIds = createResponse.Feeds.Select(f => f.Feed).ToList();
await client.Feeds.DeleteFeedsBatchAsync(new DeleteFeedsBatchRequest
{
  Feeds = feedIds,
  HardDelete = true,
});
```

```python label="Python"
feeds = [
    {
        "feed_id": f"feed_{i}",
        "feed_group_id": "user",
        "name": f"Feed {i}",
        "visibility": "public",
    }
    for i in range(100)
]
response = client.feeds.create_feeds_batch(feeds=feeds)
feed_ids = [f["feed"] for f in response["feeds"]]
client.feeds.delete_feeds_batch(feeds=feed_ids, hard_delete=True)
```

```ruby label="Ruby"
feeds = 100.times.map do |i|
  GetStream::Generated::Models::FeedRequest.new(
    feed_id: "feed_#{i}",
    feed_group_id: 'user',
    name: "Feed #{i}",
    visibility: 'public'
  )
end
response = client.feeds.create_feeds_batch(
  GetStream::Generated::Models::CreateFeedsBatchRequest.new(feeds: feeds)
)
feed_ids = response.feeds.map(&:feed)
client.feeds.delete_feeds_batch(
  GetStream::Generated::Models::DeleteFeedsBatchRequest.new(
    feeds: feed_ids,
    hard_delete: true
  )
)
```

</Tabs>


---

This page was last updated at 2026-06-05T14:24:09.699Z.

For the most recent version of this documentation, visit [https://getstream.io/activity-feeds/docs/dotnet-csharp/feeds/](https://getstream.io/activity-feeds/docs/dotnet-csharp/feeds/).