# Activity selectors

Activity selectors give you control over which data is shown in a feed. For example you can decide to show only popular activities, or activities that fit the current user's interests.

<admonition type="info">

Each activity selector selects the first 1000 activities that match its selection criteria.

If `cutoff_window` is not set (and the selector has no built-in default — see the parameter tables below), no time-based filter is applied. The selector returns the latest 1000 matching activities regardless of when they were created — effectively `cutoff_window = infinity`, capped only by the 1000-activity limit.

The minimum accepted value for `cutoff_window` is `1h`. Supported units are `s`, `m`, `h`, `d`, `w`, `y`. `cutoff_window` cannot be set together with `cutoff_time`.

</admonition>

This page details what kind of activity selectors are supported by the Stream API, and how can you configure them.

You can create custom feed groups or update the built-in groups with your own activity selector configuration.

## Selector types

### Current Feed Selector

Shows activities from the current feed. This is the selector used when reading a `user` feed.

#### Parameters

| Name            | Type                               | Description                                         | Default      | Required |
| --------------- | ---------------------------------- | --------------------------------------------------- | ------------ | -------- |
| `sort`          | array, see [Sort](#sort)           | Sort options                                        | Newest first | No       |
| `filter`        | object, see [Filters](#filters)    | Additional filter conditions                        | -            | No       |
| `cutoff_window` | string, duration like `2h` or `5d` | Activities older than this window won't be selected | -            | No       |

#### Scope

The feed we're currently reading

#### Example

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "current_feed",
      // Optionally provide cutoff window
      cutoff_window: "10d",
    },
  ],
});
```

```go label="Go"
activitySelector := getstream.ActivitySelectorConfig{
  Type:             getstream.PtrTo("current_feed"),
  // Optionally provide cutoff window
  CutoffWindow: getstream.PtrTo("10d"),
}

request := &getstream.CreateFeedGroupRequest{
  ID:                "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{activitySelector},
}

ctx := context.Background()
response, err := client.Feeds().CreateFeedGroup(ctx, request)
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "current_feed",
                cutoffWindow: "10d"
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

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

client = GetStreamRuby.manual(
  api_key: 'api_key',
  api_secret: 'api_secret'
)

create_request = GetStream::Generated::Models::CreateFeedGroupRequest.new(
  id: 'myid',
  activity_selectors: [
    GetStream::Generated::Models::ActivitySelectorConfig.new(
      type: 'current_feed',
      cutoff_window: '10d'
    )
  ]
)

create_response = client.feeds.create_feed_group(create_request)
```

</Tabs>

### Following Feed Selector

Shows activities from followed feeds and activities directly added to the feed being read. This is the selector used when reading a `timeline` feed.

#### Parameters

| Name            | Type                               | Description                                         | Default | Required |
| --------------- | ---------------------------------- | --------------------------------------------------- | ------- | -------- |
| `filter`        | object, see [Filters](#filters)    | Additional filter conditions                        | -       | No       |
| `cutoff_window` | string, duration like `2h` or `5d` | Activities older than this window won't be selected | -       | No       |

#### Scope

1 000 latest added activities from feeds followed by the feed we're currently reading, and activities posted to the current feed.

#### Example

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "following",
      // Optionally provide cutoff window
      cutoff_window: "10d",
    },
  ],
});
```

```go label="Go"
activitySelector := getstream.ActivitySelectorConfig{
  Type:             getstream.PtrTo("following"),
  // Optionally provide cutoff window
  CutoffWindow: getstream.PtrTo("10d"),
}

request := &getstream.CreateFeedGroupRequest{
  ID:                "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{activitySelector},
}

ctx := context.Background()
response, err := client.Feeds().CreateFeedGroup(ctx, request)
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "following",
                cutoffWindow: "10d"
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

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

client = GetStreamRuby.manual(
  api_key: 'api_key',
  api_secret: 'api_secret'
)

create_request = GetStream::Generated::Models::CreateFeedGroupRequest.new(
  id: 'myid',
  activity_selectors: [
    GetStream::Generated::Models::ActivitySelectorConfig.new(
      type: 'following',
      cutoff_window: '10d'
    )
  ]
)

create_response = client.feeds.create_feed_group(create_request)
```

</Tabs>

### Popular Activity Selector

Selects popular activities from public and visible feeds.

Popularity is computed by the following formula:

```js
activity.popularity = reactions + comments * 2 + bookmarks * 3 + shares * 3;
```

#### Parameters

| Name             | Type                                        | Description                                               | Default            | Required |
| ---------------- | ------------------------------------------- | --------------------------------------------------------- | ------------------ | -------- |
| `sort`           | array, see [Sort](#sort)                    | Sort options                                              | Newest first       | No       |
| `filter`         | object, see [Filters](#filters)             | Additional filter conditions                              | -                  | No       |
| `min_popularity` | number (only positive numbers are accepted) | Minimum popularity an activity should have to be selected | `5`                | No       |
| `cutoff_window`  | string, duration like `2h` or `5d`          | Activities older than this window won't be selected       | `7d` (last 7 days) | No       |

#### Scope

Any feed with `public` or `visible` visibility level.

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "popular",
      // Optional parameters
      min_popularity: 70,
      cutoff_window: "10d",
    },
  ],
});
```

```go label="Go"
activitySelector := getstream.ActivitySelectorConfig{
  Type:             getstream.PtrTo("popular"),
  // Optional parameters
  CutoffWindow:     getstream.PtrTo("10d"),
  MinPopularity:    getstream.PtrTo(70),
}

request := &getstream.CreateFeedGroupRequest{
  ID:                "myid10",
  ActivitySelectors: []getstream.ActivitySelectorConfig{activitySelector},
}

ctx := context.Background()
response, err := client.Feeds().CreateFeedGroup(ctx, request)
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "popular",
                minPopularity: 70,
                cutoffWindow: "10d"
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

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

client = GetStreamRuby.manual(
  api_key: 'api_key',
  api_secret: 'api_secret'
)

create_request = GetStream::Generated::Models::CreateFeedGroupRequest.new(
  id: 'myid',
  activity_selectors: [
    GetStream::Generated::Models::ActivitySelectorConfig.new(
      type: 'popular',
      min_popularity: 70,
      cutoff_window: '10d'
    )
  ]
)

create_response = client.feeds.create_feed_group(create_request)
```

</Tabs>

### Proximity Activity Selector

Shows activities based on geographic proximity

#### Parameters

| Name             | Type                                        | Description                                               | Default      | Required |
| ---------------- | ------------------------------------------- | --------------------------------------------------------- | ------------ | -------- |
| `sort`           | array, see [Sort](#sort)                    | Sort options                                              | Newest first | No       |
| `filter`         | object, see [Filters](#filters)             | Additional filter conditions                              | -            | No       |
| `cutoff_window`  | string, duration like `2h` or `5d`          | Activities older than this window won't be selected       | -            | No       |
| `min_popularity` | number (only positive numbers are accepted) | Minimum popularity an activity should have to be selected | `0`          | No       |

#### Scope

Any feed with `public` or `visible` visibility level.

#### Example

<Tabs>

```swift label="Swift"

```

```js label="JavaScript"
// Provide filter when reading the feed
await feed.getOrCreate({
  filter: {
    within_bounds: {
      $eq: {
        ne_lat: 52.43017,
        ne_lng: 4.924821,
        sw_lat: 52.334272,
        sw_lng: 4.822116,
      },
    },
    // Optionally provide other filter properties, just like when querying activities
    activity_type: "hike",
  },
});

// Use either within_bounds or near filter
await feed.getOrCreate({
  filter: {
    near: { $eq: { lat: 52.373558, lng: 4.885261, distance: 10 } },
  },
});

// You can omit filter if location is set for user
const me = client.state.getLatestValue().connected_user;
await client.updateUsersPartial({
  users: [
    {
      id: me.id,
      set: {
        lat: 52.373558,
        lng: 4.885261,
      },
    },
  ],
});

await feed.getOrCreate({
  // If filter is not provided, but feed group has proximity selector, API searches based on user's location
});
```

```js label="React"
// Provide filter when reading the feed
await feed.getOrCreate({
  filter: {
    within_bounds: {
      $eq: {
        ne_lat: 52.43017,
        ne_lng: 4.924821,
        sw_lat: 52.334272,
        sw_lng: 4.822116,
      },
    },
    // Optionally provide other filter properties, just like when querying activities
    activity_type: "hike",
  },
});

// Use either within_bounds or near filter
await feed.getOrCreate({
  filter: {
    near: { $eq: { lat: 52.373558, lng: 4.885261, distance: 10 } },
  },
});

// You can omit filter if location is set for user
const me = client.state.getLatestValue().connected_user;
await client.updateUsersPartial({
  users: [
    {
      id: me.id,
      set: {
        lat: 52.373558,
        lng: 4.885261,
      },
    },
  ],
});

await feed.getOrCreate({
  // If filter is not provided, but feed group has proximity selector, API searches based on user's location
});
```

```js label="React Native"
// Provide filter when reading the feed
await feed.getOrCreate({
  filter: {
    within_bounds: {
      $eq: {
        ne_lat: 52.43017,
        ne_lng: 4.924821,
        sw_lat: 52.334272,
        sw_lng: 4.822116,
      },
    },
    // Optionally provide other filter properties, just like when querying activities
    activity_type: "hike",
  },
});

// Use either within_bounds or near filter
await feed.getOrCreate({
  filter: {
    near: { $eq: { lat: 52.373558, lng: 4.885261, distance: 10 } },
  },
});

// You can omit filter if location is set for user
const me = client.state.getLatestValue().connected_user;
await client.updateUsersPartial({
  users: [
    {
      id: me.id,
      set: {
        lat: 52.373558,
        lng: 4.885261,
      },
    },
  ],
});

await feed.getOrCreate({
  // If filter is not provided, but feed group has proximity selector, API searches based on user's location
});
```

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "proximity",
      // Optional parameters
      min_popularity: 40,
      cutoff_window: "1d",
    },
  ],
});

// Provide filter when reading the feed
await feed.getOrCreate({
  filter: {
    within_bounds: {
      $eq: {
        ne_lat: 52.43017,
        ne_lng: 4.924821,
        sw_lat: 52.334272,
        sw_lng: 4.822116,
      },
    },
  },
  user_id: "<user id>",
});

// Use either within_bounds or near filter
await feed.getOrCreate({
  filter: {
    near: { $eq: { lat: 52.373558, lng: 4.885261, distance: 10 } },
  },
  user_id: "<user id>",
});
```

```go label="Go"
activitySelector := getstream.ActivitySelectorConfig{
  Type:             getstream.PtrTo("proximity"),
  // Optional parameters
  CutoffWindow:     getstream.PtrTo("1d"),
  MinPopularity:    getstream.PtrTo(40),
}

request := &getstream.CreateFeedGroupRequest{
  ID:                "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{activitySelector},
}

// Create the feed group
ctx := context.Background()
response, err := client.Feeds().CreateFeedGroup(ctx, request)
if err != nil {
  log.Fatal("Error creating feed group:", err)
}

// Reading feed
ctx := context.Background()
feed := client.Feeds().Feed("user", "john")
response, err := feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  Filter:  map[string]any{
    "within_bounds": map[string]any{
      "$eq": map[string]any{
        "ne_lat": 52.43017,
        "ne_lng": 4.924821,
        "sw_lat": 52.334272,
        "sw_lng": 4.822116,
      },
    },
  },
  UserID:  getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error:", err)
}

response, err = feed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  Filter:  map[string]any{
    "near": map[string]any{
      "$eq": map[string]any{
        "lat":      52.373558,
        "lng":      4.885261,
        "distance": 10,
      },
    },
  },
  UserID:  getstream.PtrTo("john"),
})

if err != nil {
  log.Fatal("Error:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "proximity",
                minPopularity: 40,
                cutoffWindow: "1d",
            )
        ]
    )
);

// Provide filter when reading the feed
$feedResponse = $feedsClient->getOrCreateFeed(
    "myid",
    "user123",
    new GeneratedModels\GetOrCreateFeedRequest(
        filter: (object)[
            'within_bounds' => (object)[
                '$eq' => (object)[
                    'ne_lat' => 52.43017,
                    'ne_lng' => 4.924821,
                    'sw_lat' => 52.334272,
                    'sw_lng' => 4.822116,
                ]
            ]
        ],
        userID: "user123"
    )
);

// Use either within_bounds or near filter
$feedResponse2 = $feedsClient->getOrCreateFeed(
    "myid",
    "user123",
    new GeneratedModels\GetOrCreateFeedRequest(
        filter: (object)[
            'near' => (object)[
                '$eq' => (object)[
                    'lat' => 52.373558,
                    'lng' => 4.885261,
                    'distance' => 10
                ]
            ]
        ],
        userID: "user123"
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

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

client = GetStreamRuby.manual(
  api_key: 'api_key',
  api_secret: 'api_secret'
)

create_request = GetStream::Generated::Models::CreateFeedGroupRequest.new(
  id: 'myid',
  activity_selectors: [
    GetStream::Generated::Models::ActivitySelectorConfig.new(
      type: 'interest',
      sort: [{ field: 'popularity', direction: -1 }],
      filter: { activity_type: 'post' }
    )
  ]
)

create_response = client.feeds.create_feed_group(create_request)
```

</Tabs>

### Interest Activity Selector

Selects activities that match the logged-in user's interests. Interests are automatically calculated for each user by Stream API based on which activities the user interacts with.

<admonition type="info">

Interests are based on activity topics. Topics are stored in the `interest_tags` field of an activity. It can be computed automatically using [activity processors](/activity-feeds/docs/go-golang/activity-processors/) or set when [creating an activity](/activity-feeds/docs/go-golang/activities/#overview-of-all-activity-fields).

</admonition>

#### Parameters

| Name             | Type                                        | Description                                               | Default            | Required |
| ---------------- | ------------------------------------------- | --------------------------------------------------------- | ------------------ | -------- |
| `sort`           | array, see [Sort](#sort)                    | Sort options                                              | Newest first       | No       |
| `filter`         | object, see [Filters](#filters)             | Additional filter conditions                              | -                  | No       |
| `cutoff_window`  | string, duration like `2h` or `5d`          | Activities older than this window won't be selected       | `2d` (last 2 days) | No       |
| `min_popularity` | number (only positive numbers are accepted) | Minimum popularity an activity should have to be selected | `0`                | No       |

#### Scope

Feeds that are visible to the logged-in user.

#### Examples

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "interest",
      // Optional parameters
      sort: [{ field: "popularity", direction: -1 }],
      filter: {
        activity_type: "post",
      },
    },
  ],
});
```

```go label="Go"
response, err := client.Feeds().CreateFeedGroup(context.Background(), &getstream.CreateFeedGroupRequest{
  ID: "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{
    {
      Type: getstream.PtrTo("interest"),
      // Optional parameters
      Sort: []getstream.SortParam{
        {
          Field:     getstream.PtrTo("popularity"),
          Direction: getstream.PtrTo(-1),
        },
      },
      Filter: map[string]any{
        "activity_type": "post",
      },
    },
  },
})
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "interest",
                sort: [
                    ['field' => 'popularity', 'direction' => -1]
                ],
                filter: (object)[
                    'activity_type' => 'post'
                ]
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

</Tabs>

### Query Activity Selector

Selects activities using the provided filter query.

#### Parameters

| Name            | Type                               | Description                                         | Default            | Required |
| --------------- | ---------------------------------- | --------------------------------------------------- | ------------------ | -------- |
| `sort`          | array, see [Sort](#sort)           | Sort options                                        | Newest first       | No       |
| `filter`        | object, see [Filters](#filters)    | Additional filter conditions                        | -                  | No       |
| `cutoff_window` | string, duration like `2h` or `5d` | Activities older than this window won't be selected | `7d` (last 7 days) | No       |

#### Scope

Feeds that are visible to the logged-in user.

#### Example

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "query",
      filter: { search_data: { $contains: { workout_type: "hike" } } },
      // Optional parameters
      sort: [{ field: "created_at", direction: -1 }],
    },
  ],
});
```

```go label="Go"
response, err := client.Feeds().CreateFeedGroup(context.Background(), &getstream.CreateFeedGroupRequest{
  ID: "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{
    {
      Type: getstream.PtrTo("query"),
      Filter: map[string]any{
        "search_data": map[string]any{
          "$contains": map[string]any{
            "workout_type": "hike",
          },
        },
      },
      // Optional parameters
      Sort: []getstream.SortParam{
        {
          Field:     getstream.PtrTo("created_at"),
          Direction: getstream.PtrTo(-1),
        },
      },
    },
  },
})
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "query",
                filter: (object)[
                    'search_data' => (object)[
                        '$contains' => (object)[
                            'workout_type' => 'hike'
                        ]
                    ]
                ],
                sort: [
                    ['field' => 'created_at', 'direction' => -1]
                ]
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

</Tabs>

### Follow Suggestion Activity Selector

Selects activities from feeds that are suggested to the user based on follow suggestions. This selector uses Stream's intelligent follow suggestion algorithm to discover relevant content from feeds the user might want to follow.

<admonition type="info">

This selector requires an authenticated user. It will return empty results for anonymous users.

</admonition>

#### Parameters

| Name                  | Type                                        | Description                                                           | Default          | Required |
| --------------------- | ------------------------------------------- | --------------------------------------------------------------------- | ---------------- | -------- |
| `sort`                | array, see [Sort](#sort)                    | Sort options                                                          | Popularity first | No       |
| `cutoff_window`       | string, duration like `2h` or `5d`          | Activities older than this window won't be selected                   | -                | No       |
| `min_popularity`      | number (only positive numbers are accepted) | Minimum popularity an activity should have to be selected             | `5`              | No       |
| `activities_per_feed` | number (1-100)                              | Number of activities to select from each suggested feed               | `2`              | No       |
| `max_suggested_feeds` | number (1-50)                               | Maximum number of suggested feeds to consider                         | `10`             | No       |
| `min_feed_score`      | number (0.0-1.0)                            | Minimum recommendation score for a feed to be included in suggestions | `0.3`            | No       |

#### Scope

Activities from user feeds that are suggested based on the [follow suggestion algorithm](/activity-feeds/docs/go-golang/follows/#follow-suggestions). The selector always suggests feeds from the "user" feed group, regardless of which feed group is being viewed.

#### Example

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    {
      type: "follow_suggestion",
      // Optional parameters
      min_popularity: 10,
      cutoff_window: "10d",
      params: {
        activities_per_feed: 3,
        max_suggested_feeds: 15,
        min_feed_score: 0.4,
      },
    },
  ],
});
```

```go label="Go"
activitySelector := getstream.ActivitySelectorConfig{
  Type:             getstream.PtrTo("follow_suggestion"),
  // Optional parameters
  CutoffWindow:     getstream.PtrTo("10d"),
  MinPopularity:    getstream.PtrTo(10),
  Params: map[string]any{
    "activities_per_feed": 3,
    "max_suggested_feeds": 15,
    "min_feed_score": 0.4,
  },
}

request := &getstream.CreateFeedGroupRequest{
  ID:                "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{activitySelector},
}

ctx := context.Background()
response, err := client.Feeds().CreateFeedGroup(ctx, request)
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "follow_suggestion",
                minPopularity: 10,
                cutoffWindow: "10d",
                params: (object)[
                    'activities_per_feed' => 3,
                    'max_suggested_feeds' => 15,
                    'min_feed_score' => 0.4
                ]
            )
        ]
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivitySelectors = new List<ActivitySelectorConfig>
    {
        new ActivitySelectorConfig
        {
            Type = "follow_suggestion",
            MinPopularity = 10,
            CutoffWindow = "10d",
            Params = new Dictionary<string, object>
            {
                { "activities_per_feed", 3 },
                { "max_suggested_feeds", 15 },
                { "min_feed_score", 0.4 }
            }
        }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id="myid",
    activity_selectors=[
        {
            "type": "follow_suggestion",
            "min_popularity": 10,
            "cutoff_window": "10d",
            "params": {
                "activities_per_feed": 3,
                "max_suggested_feeds": 15,
                "min_feed_score": 0.4,
            },
        }
    ],
)
```

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

client = GetStreamRuby.manual(
  api_key: 'api_key',
  api_secret: 'api_secret'
)

create_request = GetStream::Generated::Models::CreateFeedGroupRequest.new(
  id: 'myid',
  activity_selectors: [
    GetStream::Generated::Models::ActivitySelectorConfig.new(
      type: 'follow_suggestion',
      min_popularity: 10,
      cutoff_window: '10d',
      params: {
        activities_per_feed: 3,
        max_suggested_feeds: 15,
        min_feed_score: 0.4
      }
    )
  ]
)

create_response = client.feeds.create_feed_group(create_request)
```

</Tabs>

## Selector parameters

Supported values for `sort` and `filter` objects.

### Sort

The following `sort` options are available:

<admonition type="info">

Please note that sort options are only used to determine which activities are selected. The final activity order is determined by [ranking](/activity-feeds/docs/go-golang/custom-ranking/).

</admonition>

Fields:

- `created_at`
- `popularity`

Direction: `1` or `-1`


### Filters

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>


It's also possible to provide filters when reading a feed. When providing `filter` to read a feed, group-level filters are ignored.

## Combining selectors

You can combine multiple selectors. The example below will include:

- popular activities from public and visible feeds
- activities from feeds the user follows
- activities from feeds the user has access to, and match the user's interest

Each activity selector selects the first 1000 activities that match its selection criteria. We suggest taking adventage of the different config options for the selectors to make sure that the selectors return the most relevant activities. When combining selectors, every selector runs independently, so in this case 3 \* 1000 activities can be selected.

Once selectors run, ranking is applied to the activities that are returned by the selectors. Even if you have multiple selectors, ranking only runs once.

You can't use more than 3 selectors in a feed group/view configuration.

<Tabs>

```js label="Node"
const response = await client.feeds.createFeedGroup({
  id: "myid",
  activity_selectors: [
    { type: "popular" },
    { type: "following" },
    { type: "interest" },
  ],

  ranking: { type: "expression", score: "decay_linear(time) * popularity" },
});
```

```go label="Go"
response, err := client.Feeds().CreateFeedGroup(context.Background(), &getstream.CreateFeedGroupRequest{
  ID: "myid",
  ActivitySelectors: []getstream.ActivitySelectorConfig{
    {
      Type: getstream.PtrTo("popular"),
    },
    {
      Type: getstream.PtrTo("following"),
    },
    {
      Type: getstream.PtrTo("interest"),
    },
  },
  Ranking: &getstream.RankingConfig{
    Type: getstream.PtrTo("expression"),
    Score: getstream.PtrTo("decay_linear(time) * popularity"),
  },
})
if err != nil {
  log.Fatal("Error creating feed group:", err)
}
```

```php label="php"
$createResponse = $feedsClient->createFeedGroup(
    new GeneratedModels\CreateFeedGroupRequest(
        id: "myid",
        activitySelectors: [
            new GeneratedModels\ActivitySelectorConfig(
                type: "popular"
            ),
            new GeneratedModels\ActivitySelectorConfig(
                type: "following"
            ),
            new GeneratedModels\ActivitySelectorConfig(
                type: "interest"
            )
        ],
        ranking: new GeneratedModels\RankingConfig(
            type: "expression",
            score: "decay_linear(time) * popularity"
        )
    )
);
```

```csharp label="C#"
var createResponse = await _feedsV3Client.CreateFeedGroupAsync(new CreateFeedGroupRequest
{
    ID = feedGroupId,
    DefaultVisibility = "public",
    ActivityProcessors = new List<ActivityProcessorConfig>
    {
        new() { Type = "default" }
    }
});
```

```python label="Python"
create_response = self.client.feeds.create_feed_group(
    id=feed_group_id,
    default_visibility="public",
)
```

</Tabs>

## Real-time updates for activity selectors

For activities selected by `current` and `following` selectors WebSocket events are delivered to clients (as long as `watch: true` is used to read the feed). However, for activities selected by other selectors, WebSocket events are not delivered. This means you won't be receiving `activity.new` events, or updates if other users like/comment/etc. on the activity.

If you combine activity selectors, you'll be still receiving WebSocket events for activities selected by `current` and `following` selectors.

## Experimenting with selectors

Feed groups let you define what activities should be included in the feed and the ranking to sort these activities.

By default all feeds in the given group will have the same settings. However, you might want to experiment with different selectors and rankings. Feed views let you do that by overriding the group's default settings.

<admonition type="info">

Note that any write operation to feed groups/views can take up to 30 seconds to propagate to all API nodes.

</admonition>



---

This page was last updated at 2026-05-22T16:31:46.543Z.

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