# Follow and Unfollow

## Follow

The source feed should have a group that has "following" activity selector enabled, for example the built-in `timeline` group. The target feed should have a group that has "current" activity selector enabled, for example the built-in `user` group.

<Tabs>

```swift label="Swift"
// Follow a user
let timeline = client.feed(group: "timeline", id: "john")
_ = try await timeline.follow(FeedId(group: "user", id: "tom"))

// Follow a stock
_ = try await timeline.follow(FeedId(group: "stock", id: "apple"))

// Follow with more fields
_ = try await timeline.follow(
    FeedId(group: "stock", id: "apple"),
    custom: ["reason": "investment"]
)
```

```kotlin label="Kotlin"
// Follow a user
val timeline = client.feed(group = "timeline", id = "john")
timeline.follow(FeedId(group = "user", id = "tom"))

// Follow a stock
timeline.follow(FeedId(group = "stock", id = "apple"))

// Follow with more fields
timeline.follow(
    targetFid = FeedId(group = "stock", id = "apple"),
    custom = mapOf("reason" to "investment")
)
```

```js label="JavaScript"
const timeline = client.feed("timeline", "john");
await timeline.getOrCreate();

// Follow a user
await timeline.follow("user:tom");

// Follow a stock
await timeline.follow("stock:apple");

// Follow with more fields
await timeline.follow("stock:apple", {
  push_preference: "all",
  custom: {
    reason: "investment",
  },
});

// Follow without Feed instance
await client.follow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```js label="React"
const timeline = client.feed("timeline", "john");
await timeline.getOrCreate();

// Follow a user
await timeline.follow("user:tom");

// Follow a stock
await timeline.follow("stock:apple");

// Follow with more fields
await timeline.follow("stock:apple", {
  push_preference: "all",
  custom: {
    reason: "investment",
  },
});

// Follow without Feed instance
await client.follow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```js label="React Native"
const timeline = client.feed("timeline", "john");
await timeline.getOrCreate();

// Follow a user
await timeline.follow("user:tom");

// Follow a stock
await timeline.follow("stock:apple");

// Follow with more fields
await timeline.follow("stock:apple", {
  push_preference: "all",
  custom: {
    reason: "investment",
  },
});

// Follow without Feed instance
await client.follow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```dart label="Dart"
// Follow a user
final timeline = client.feed(group: 'timeline', id: 'john');
await timeline.follow(targetFid: const FeedId(group: 'user', id: 'tom'));
// Follow a stock
await timeline.follow(targetFid: const FeedId(group: 'stock', id: 'apple'));
// Follow with more fields
await timeline.follow(
  targetFid: const FeedId(group: 'stock', id: 'apple'),
  custom: {'reason': 'investment'},
);
```

```js label="Node"
const timeline = client.feeds.feed("timeline", "john");
await timeline.getOrCreate({
  user_id: "john",
});

// Follow a user
await client.feeds.follow({
  source: timeline.feed,
  target: "user:tom",
});

// Follow a stock
await client.feeds.follow({
  source: timeline.feed,
  target: "stock:apple",
});

// Follow with more fields
await client.feeds.follow({
  source: timeline.feed,
  target: "stock:apple",
  push_preference: "all",
  custom: {
    reason: "investment",
  },
});
```

```go label="Go"
// Create timeline feed
timeline := client.Feeds().Feed("timeline", "john")
  _, err = timeline.GetOrCreate(context.Background(), &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error getting/creating timeline feed:", err)
}
log.Println("Timeline feed created/retrieved successfully")

// Follow a user
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
  Source: "timeline:john",
  Target: "user:tom",
})
if err != nil {
  log.Fatal("Error following user:", err)
}
log.Println("Successfully followed user:tom")

// Follow a stock
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
  Source: "timeline:john",
  Target: "stock:apple",
})
if err != nil {
  log.Fatal("Error following stock:", err)
}
log.Println("Successfully followed stock:apple")

// Follow with more fields
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
  Source:         "timeline:john",
  Target:         "stock:apple",
  PushPreference: getstream.PtrTo("all"),
  Custom: map[string]any{
    "reason": "investment",
  },
})
if err != nil {
  log.Fatal("Error following stock with custom fields:", err)
}
log.Println("Successfully followed stock:apple with custom fields")
```

```java label="Java"
// Follow a user
FollowRequest followRequest =
    FollowRequest.builder()
        .source(USER_FEED_TYPE + testUserId)
        .target(USER_FEED_TYPE + testUserId2)
        .build();

SingleFollowResponse response = feeds.follow(followRequest).execute().getData();
```

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

$response = $feedsClient->follow(
    new GeneratedModels\FollowRequest(
        source: 'timeline:john',
        target: 'user:tom'
    )
);

// Follow a stock
$response = $feedsClient->follow(
    new GeneratedModels\FollowRequest(
        source: 'timeline:john',
        target: 'stock:apple'
    )
);

// Follow with more fields
$response = $feedsClient->follow(
    new GeneratedModels\FollowRequest(
        source: 'timeline:john',
        target: 'stock:apple',
        pushPreference: 'all',
        custom: (object)['reason' => 'investment']
    )
);
```

```csharp label="C#"
// Follow a user
var response = await _feedsV3Client.FollowAsync(
    new FollowRequest
    {
        Source = $"user:{_testFeedId}",
        Target = $"user:{_testFeedId2}"
    }
);
```

```python label="Python"
# Follow a user
response = self.client.feeds.follow(
    source=f"{self.USER_FEED_TYPE}:{self.test_user_id}",
    target=f"{self.USER_FEED_TYPE}:{self.test_user_id_2}",
)
```

```ruby label="Ruby"
# Follow a user
follow_request = {
  source: 'user:user123',
  target: 'user:user456'
}

client.feeds.follow(follow_request)
```

</Tabs>

Trying to follow a feed that is already followed will result in an error. You can also use the [`getOrCreateFollows`](#batch-follow--unfollow) endpoint that provides an idempotent batch follow mechanism.

<admonition type="info" title="Auto-creating feeds on follow">

You do not need to call `getOrCreate` on the source or target feed before following. If either feed does not exist yet, the feed is created automatically when you call follow. This applies to both the single follow and batch follow endpoints.

</admonition>

### Auto-creating users

`follow`, `followBatch`, and `getOrCreateFollows` support an opt-in `create_users` flag (default: `false`).

- Server-side only: client-side callers that set `create_users` are ignored and still receive `user not found` if referenced users are missing.
- Missing users only: `create_users` creates users that do not exist yet; it does not update existing users.
- Best-effort behavior: missing-user creation happens before follow creation and is not transactional with the follow write. A user can be created even if a later follow validation fails.
- User ID inference: when `create_users` is `true`, user IDs are derived from the feed IDs in `source` and `target` (for example, `timeline:alice` infers `alice`, `user:bob` infers `bob`).

For a single `follow` call, set `create_users` on the request body.

<Tabs>

```js label="Node"
await client.feeds.follow({
  source: "timeline:alice",
  target: "user:bob",
  create_users: true,
});
```

```go label="Go"
_, err = client.Feeds().Follow(context.Background(), &getstream.FollowRequest{
  Source:      "timeline:alice",
  Target:      "user:bob",
  CreateUsers: getstream.PtrTo(true),
})
if err != nil {
  log.Fatal("Error creating follow with create_users:", err)
}
```

```java label="Java"
FollowRequest request = FollowRequest.builder()
    .source("timeline:alice")
    .target("user:bob")
    .createUsers(true)
    .build();

feeds.follow(request).execute();
```

```php label="php"
$response = $feedsClient->follow(
    new GeneratedModels\FollowRequest(
        source: 'timeline:alice',
        target: 'user:bob',
        createUsers: true
    )
);
```

```csharp label="C#"
await _feedsV3Client.FollowAsync(
    new FollowRequest
    {
        Source = "timeline:alice",
        Target = "user:bob",
        CreateUsers = true
    }
);
```

```python label="Python"
response = self.client.feeds.follow(
    source="timeline:alice",
    target="user:bob",
    create_users=True,
)
```

```ruby label="Ruby"
follow_request = {
  source: "timeline:alice",
  target: "user:bob",
  create_users: true
}

client.feeds.follow(follow_request)
```

</Tabs>

For `followBatch` / `getOrCreateFollows`, set `create_users` only at the top level. `follows[i].create_users` is rejected.

<Tabs>

```js label="Node"
await client.feeds.getOrCreateFollows({
  create_users: true,
  follows: [
    { source: "timeline:alice", target: "user:bob" },
    { source: "timeline:alice", target: "user:charlie" },
  ],
});
```

```go label="Go"
response, err := client.Feeds().GetOrCreateFollows(context.Background(), &getstream.GetOrCreateFollowsRequest{
  CreateUsers: getstream.PtrTo(true),
  Follows: []getstream.FollowRequest{
    {
      Source: "timeline:alice",
      Target: "user:bob",
    },
    {
      Source: "timeline:alice",
      Target: "user:charlie",
    },
  },
})
if err != nil {
  log.Fatal("Error creating follows with create_users:", err)
}
fmt.Printf("Created follows: %d\n", len(response.Data.Created))
```

```java label="Java"
GetOrCreateFollowsRequest followsRequest = GetOrCreateFollowsRequest.builder()
    .createUsers(true)
    .follows(Arrays.asList(
        FollowInput.builder().source("timeline:alice").target("user:bob").build(),
        FollowInput.builder().source("timeline:alice").target("user:charlie").build()
    ))
    .build();

feeds.getOrCreateFollows(followsRequest).execute();
```

```php label="php"
$response = $feedsClient->getOrCreateFollows(
    new GeneratedModels\GetOrCreateFollowsRequest(
        createUsers: true,
        follows: [
            new GeneratedModels\FollowInput(
                source: 'timeline:alice',
                target: 'user:bob'
            ),
            new GeneratedModels\FollowInput(
                source: 'timeline:alice',
                target: 'user:charlie'
            )
        ]
    )
);
```

```csharp label="C#"
await _feedsV3Client.GetOrCreateFollowsAsync(
    new GetOrCreateFollowsRequest
    {
        CreateUsers = true,
        Follows = new List<FollowInput>
        {
            new FollowInput { Source = "timeline:alice", Target = "user:bob" },
            new FollowInput { Source = "timeline:alice", Target = "user:charlie" }
        }
    }
);
```

```python label="Python"
response = self.client.feeds.get_or_create_follows(
    create_users=True,
    follows=[
        {"source": "timeline:alice", "target": "user:bob"},
        {"source": "timeline:alice", "target": "user:charlie"},
    ],
)
```

```ruby label="Ruby"
response = client.feeds.get_or_create_follows(
  GetStream::Generated::Models::GetOrCreateFollowsRequest.new(
    create_users: true,
    follows: [
      GetStream::Generated::Models::FollowInput.new(
        source: "timeline:alice",
        target: "user:bob"
      ),
      GetStream::Generated::Models::FollowInput.new(
        source: "timeline:alice",
        target: "user:charlie"
      )
    ]
  )
)
```

</Tabs>

## Unfollow

When unfollowing a feed, all previous activities of that feed are removed from the timeline.

Trying to unfollow a feed that is not followed, will result in an error. You can also use the [`getOrCreateUnfollows`](#batch-follow--unfollow) endpoint that provides an idempotent batch unfollow mechanism.

<Tabs>

```swift label="Swift"
let timeline = client.feed(group: "timeline", id: "john")
try await timeline.unfollow(FeedId(group: "user", id: "tom"))
```

```kotlin label="Kotlin"
val timeline = client.feed(group = "timeline", id = "john")
timeline.unfollow(FeedId(group = "user", id = "tom"))
```

```js label="JavaScript"
const timeline = client.feed("timeline", "john");
await timeline.unfollow("user:tom");

// Unfollow without Feed instance
await client.unfollow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```js label="React"
const timeline = client.feed("timeline", "john");
await timeline.unfollow("user:tom");

// Unfollow without Feed instance
await client.unfollow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```js label="React Native"
const timeline = client.feed("timeline", "john");
await timeline.unfollow("user:tom");

// Unfollow without Feed instance
await client.unfollow({
  source: "timeline:alice",
  target: "user:tom",
});
```

```dart label="Dart"
final timeline = client.feed(group: 'timeline', id: 'john');
await timeline.unfollow(targetFid: const FeedId(group: 'user', id: 'tom'));
```

```js label="Node"
await client.feeds.unfollow({
  source: timeline.feed,
  target: "user:tom",
});
```

```go label="Go"
_, err = client.Feeds().Unfollow(context.Background(), "timeline:john", "user:tom")
```

```java label="Java"
UnfollowRequest unfollowRequest = UnfollowRequest.builder().build();
UnfollowResponse unfollowResponse =
    feeds
        .unfollow(USER_FEED_TYPE + testUserId, USER_FEED_TYPE + testUserId2, unfollowRequest)
        .execute()
        .getData();
```

```php label="php"
$response = $feedsClient->unfollow('timeline:john', 'user:tom');
```

```csharp label="C#"
var unfollowResponse = await _feedsV3Client.UnfollowAsync(
    $"user:{_testFeedId}",
    $"user:{_testFeedId3}",
    new { user_id = _testUserId }
);
```

```python label="Python"
unfollow_response = self.client.feeds.unfollow(
    f"{self.USER_FEED_TYPE}:{self.test_user_id}",
    f"{self.USER_FEED_TYPE}:{self.test_user_id_2}",
)
```

```ruby label="Ruby"
unfollow_response = client.feeds.unfollow(
  'user:user123',
  'user:user456'
)
```

</Tabs>

## Update follow

You can update an existing follow relationship (for example to change push preference, custom data, or on server-side the follower role):

The 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"
let timeline = client.feed(group: "timeline", id: sourceFeedId)
try await timeline.updateFollow(
    FeedId(group: "user", id: targetFeedId),
    pushPreference: .none,
    custom: ["note": "Updated follow"]
)
```

```kotlin label="Kotlin"
val timeline = client.feed(group = "timeline", id = sourceFeedId)
timeline.updateFollow(
    targetFid = FeedId(group = "user", id = targetFeedId),
    pushPreference = UpdateFollowRequest.PushPreference.None,
    custom = mapOf("note" to "Updated follow")
)
```

```js label="JavaScript"
await client.updateFollow({
  source: `timeline:${sourceFeedId}`,
  target: `user:${targetFeedId}`,
  push_preference: "none",
  custom: {
    note: "Updated follow",
  },
});
```

```js label="React"
await client.updateFollow({
  source: `timeline:${sourceFeedId}`,
  target: `user:${targetFeedId}`,
  push_preference: "none",
  custom: {
    note: "Updated follow",
  },
});
```

```js label="React Native"
await client.updateFollow({
  source: `timeline:${sourceFeedId}`,
  target: `user:${targetFeedId}`,
  push_preference: "none",
  custom: {
    note: "Updated follow",
  },
});
```

```dart label="Dart"
final timeline = client.feed(group: 'timeline', id: sourceFeedId);
await timeline.updateFollow(
  targetFid: const FeedId(group: 'user', id: targetFeedId),
  pushPreference: PushPreference.none,
  custom: {'note': 'Updated follow'},
);
```

```js label="Node"
await client.feeds.updateFollow({
  source: `timeline:${sourceFeedId}`,
  target: `user:${targetFeedId}`,
  push_preference: "none",
  role: "my_custom_feed_follower_role",
  custom: {
    note: "Updated follow",
  },
});
```

```go label="Go"
_, err = client.Feeds().UpdateFollow(context.Background(), &getstream.UpdateFollowRequest{
  Source:         "timeline:" + sourceFeedId,
  Target:         "user:" + targetFeedId,
  PushPreference: getstream.PtrTo("none"),
  FollowerRole:   getstream.PtrTo("my_custom_feed_follower_role"),
  Custom: map[string]any{
    "note": "Updated follow",
  },
})
```

```java label="Java"
UpdateFollowRequest request = UpdateFollowRequest.builder()
    .source("timeline:" + sourceFeedId)
    .target("user:" + targetFeedId)
    .pushPreference("none")
    .followerRole("my_custom_feed_follower_role")
    .custom(Map.of("note", "Updated follow"))
    .build();

feeds.updateFollow(request).execute();
```

```php label="php"
$response = $feedsClient->updateFollow(
    new GeneratedModels\UpdateFollowRequest(
        source: 'timeline:' . $sourceFeedId,
        target: 'user:' . $targetFeedId,
        pushPreference: 'none',
        followerRole: 'my_custom_feed_follower_role',
        custom: (object)['note' => 'Updated follow']
    )
);
```

```csharp label="C#"
await _feedsV3Client.UpdateFollowAsync(new UpdateFollowRequest
{
    Source = $"timeline:{sourceFeedId}",
    Target = $"user:{targetFeedId}",
    PushPreference = "none",
    FollowerRole = "my_custom_feed_follower_role",
    Custom = new Dictionary<string, object> { ["note"] = "Updated follow" },
});
```

```python label="Python"
client.feeds.update_follow(
    source=f"timeline:{source_feed_id}",
    target=f"user:{target_feed_id}",
    push_preference="none",
    follower_role="my_custom_feed_follower_role",
    custom={"note": "Updated follow"},
)
```

```ruby label="Ruby"
client.feeds.update_follow(
  source: "timeline:#{source_feed_id}",
  target: "user:#{target_feed_id}",
  push_preference: 'none',
  follower_role: 'my_custom_feed_follower_role',
  custom: { note: 'Updated follow' }
)
```

</Tabs>

## Querying Follows

<Tabs>

```swift label="Swift"
// Do I follow a list of feeds
// My feed is timeline:john
let followQuery = FollowsQuery(
    filter: .and([
        .equal(.sourceFeed, "timeline:john"),
        .in(.targetFeed, ["user:sara", "user:adam"])
    ])
)
let followList = client.followList(for: followQuery)
let page1 = try await followList.get()
let page2 = try await followList.queryMoreFollows()
let page1And2 = followList.state.follows

// Paginating through followers for a feed
// My feed is timeline:john
let followerQuery = FollowsQuery(
    filter: .equal(.targetFeed, "timeline:john")
)
let followerList = client.followList(for: followerQuery)
let followerPage1 = try await followerList.get()
```

```kotlin label="Kotlin"
// Do I follow a list of feeds
// My feed is timeline:john
val followQuery = FollowsQuery(
    filter = Filters.and(
        FollowsFilterField.sourceFeed.equal("timeline:john"),
        FollowsFilterField.targetFeed.`in`("user:sara", "user:adam")
    )
)
val followList = client.followList(query = followQuery)
val page1: Result<List<FollowData>> = followList.get()
val page2: Result<List<FollowData>> = followList.queryMoreFollows()
val page1And2 = followList.state.follows

// Paginating through followers for a feed
// My feed is timeline:john
val followerQuery = FollowsQuery(
    filter = FollowsFilterField.targetFeed.equal("timeline:john")
)
val followerList = client.followList(query = followerQuery)
val followerPage1: Result<List<FollowData>> = followerList.get()
```

```js label="JavaScript"
const myTimeline = client.feed("timeline", "john");
await myTimeline.getOrCreate();

// Do I follow a list of feeds
const response = await client.queryFollows({
  filter: {
    source_feed: myTimeline.feed,
    target_feed: { $in: ["user:sara", "user:adam"] },
  },
});

console.log(response.follows);

const userFeed = client.feed("user", "john");
await userFeed.getOrCreate();

// Paginating through followers for a feed - won't store followers in state
const firstPage = await userFeed.queryFollowers({
  limit: 20,
});

// Next page - won't store followers in state
const secondPage = await userFeed.queryFollowers({
  limit: 20,
  next: firstPage.next,
});

// or load when reading feed - will store followers in state
await feed.getOrCreate({
  followers_pagination: {
    limit: 10,
  },
});

// and then load next pages (or first if followers are not yet loaded) - will store followers in state
await feed.loadNextPageFollowers({ limit: 10 });

console.log(feed.state.getLatestValue().followers);

// Filter by source - feeds that I follow - won't store followings in state
await myTimeline.queryFollowing({ limit: 10 });

// or load when reading feed - will store followings in state
await feed.getOrCreate({
  following_pagination: {
    limit: 10,
  },
});

// and then load next pages (or first if followings are not yet loaded) - will store followings in state
await feed.loadNextPageFollowing({ limit: 10 });

console.log(feed.state.getLatestValue().following);
```

```js label="React"
const myTimeline = client.feed("timeline", "john");
await myTimeline.getOrCreate();

// Do I follow a list of feeds
const response = await client.queryFollows({
  filter: {
    source_feed: myTimeline.feed,
    target_feed: { $in: ["user:sara", "user:adam"] },
  },
});

console.log(response.follows);

const userFeed = client.feed("user", "john");
await userFeed.getOrCreate();

// Paginating through followers for a feed - won't store followers in state
const firstPage = await userFeed.queryFollowers({
  limit: 20,
});

// Next page - won't store followers in state
const secondPage = await userFeed.queryFollowers({
  limit: 20,
  next: firstPage.next,
});

// or load when reading feed - will store followers in state
await feed.getOrCreate({
  followers_pagination: {
    limit: 10,
  },
});

const {
  followers,
  follower_count,
  followers_pagination,
  is_loading_next_page,
  has_next_page,
  loadNextPage,
} = useFollowers(feed);

// and then load next pages (or first if followers are not yet loaded) - will store followers in state
await loadNextPage({ limit: 10 });

// Filter by source - feeds that I follow - won't store followings in state
await myTimeline.queryFollowing({ limit: 10 });

// or load when reading feed - will store followings in state
await feed.getOrCreate({
  following_pagination: {
    limit: 10,
  },
});

const {
  following,
  following_count,
  following_pagination,
  is_loading_next_page,
  has_next_page,
  loadNextPage,
} = useFollowing(feed);

// and then load next pages (or first if followings are not yet loaded) - will store followings in state
await loadNextPage({ limit: 10 });
```

```js label="React Native"
const myTimeline = client.feed("timeline", "john");
await myTimeline.getOrCreate();

// Do I follow a list of feeds
const response = await client.queryFollows({
  filter: {
    source_feed: myTimeline.feed,
    target_feed: { $in: ["user:sara", "user:adam"] },
  },
});

console.log(response.follows);

const userFeed = client.feed("user", "john");
await userFeed.getOrCreate();

// Paginating through followers for a feed - won't store followers in state
const firstPage = await userFeed.queryFollowers({
  limit: 20,
});

// Next page - won't store followers in state
const secondPage = await userFeed.queryFollowers({
  limit: 20,
  next: firstPage.next,
});

// or load when reading feed - will store followers in state
await feed.getOrCreate({
  followers_pagination: {
    limit: 10,
  },
});

const {
  followers,
  follower_count,
  followers_pagination,
  is_loading_next_page,
  has_next_page,
  loadNextPage,
} = useFollowers(feed);

// and then load next pages (or first if followers are not yet loaded) - will store followers in state
await loadNextPage({ limit: 10 });

// Filter by source - feeds that I follow - won't store followings in state
await myTimeline.queryFollowing({ limit: 10 });

// or load when reading feed - will store followings in state
await feed.getOrCreate({
  following_pagination: {
    limit: 10,
  },
});

const {
  following,
  following_count,
  following_pagination,
  is_loading_next_page,
  has_next_page,
  loadNextPage,
} = useFollowing(feed);

// and then load next pages (or first if followings are not yet loaded) - will store followings in state
await loadNextPage({ limit: 10 });
```

```dart label="Dart"
// Do I follow a list of feeds
// My feed is timeline:john
const followQuery = FollowsQuery(
  filter: Filter.and([
    Filter.equal(FollowsFilterField.sourceFeed, 'timeline:john'),
    Filter.in_(FollowsFilterField.targetFeed, ['user:sara', 'user:adam']),
  ]),
);
final followList = client.followList(followQuery);
final page1 = await followList.get();
final page2 = await followList.queryMoreFollows();
final page1And2 = followList.state.follows;
// Paginating through followers for a feed
// My feed is timeline:john
const followerQuery = FollowsQuery(
  filter: Filter.equal(FollowsFilterField.targetFeed, 'timeline:john'),
);
final followerList = client.followList(followerQuery);
final followerPage1 = await followerList.get();
```

```js label="Node"
const myTimeline = client.feeds.feed("timeline", "john");
await myTimeline.getOrCreate({
  user_id: "john",
});
// Do I follow a list of feeds
const response = await client.feeds.queryFollows({
  filter: {
    source_feed: myTimeline.feed,
    target_feed: { $in: ["user:sara", "user:adam"] },
  },
});

console.log(response.follows);

const userFeed = client.feeds.feed("user", "john");
await userFeed.getOrCreate({
  user_id: "john",
});

// Paginating through followers for a feed
const firstPage = await client.feeds.queryFollows({
  filter: { target_feed: userFeed.feed },
  limit: 20,
});

// Next page
const secondPage = await client.feeds.queryFollows({
  filter: { target_feed: userFeed.feed },
  limit: 20,
  next: firstPage.next,
});

// Filter by source - feeds that I follow
await client.feeds.queryFollows({
  filter: { source_feed: myTimeline.feed },
  limit: 20,
});
```

```go label="Go"
ctx := context.Background()

// Create timeline feed
myTimeline := client.Feeds().Feed("timeline", "john")
_, err = myTimeline.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error creating timeline feed:", err)
}

// Query follows to check if we follow a list of feeds
response, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
  Filter: map[string]any{
    "source_feed": "timeline:john",
    "target_feed": map[string]any{
      "$in": []string{"user:sara", "user:adam"},
    },
  },
})
if err != nil {
  log.Fatal("Error querying follows:", err)
}
log.Printf("Follows: %+v", response.Data.Follows)

// Create user feed
userFeed := client.Feeds().Feed("user", "john")
_, err = userFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("john"),
})
if err != nil {
  log.Fatal("Error creating user feed:", err)
}

// Paginating through followers for a feed - first page
firstPage, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
  Filter: map[string]any{
    "target_feed": "user:john",
  },
  Limit: getstream.PtrTo(20),
})
if err != nil {
  log.Fatal("Error querying first page of follows:", err)
}

// Next page
secondPage, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
  Filter: map[string]any{
    "target_feed": "user:john",
  },
  Limit: getstream.PtrTo(20),
  Next:  firstPage.Data.Next,
})
if err != nil {
  log.Fatal("Error querying second page of follows:", err)
}
log.Printf("First page follows: %+v", firstPage.Data.Follows)
log.Printf("Second page follows: %+v", secondPage.Data.Follows)

// Filter by source - feeds that I follow
sourceFollows, err := client.Feeds().QueryFollows(ctx, &getstream.QueryFollowsRequest{
  Filter: map[string]any{
    "source_feed": "timeline:john",
  },
  Limit: getstream.PtrTo(20),
})
if err != nil {
  log.Fatal("Error querying source follows:", err)
}
log.Printf("Source follows: %+v", sourceFollows.Data.Follows)
```

```java label="Java"
// Query follows
QueryFollowsRequest request = QueryFollowsRequest.builder().limit(10).build();
QueryFollowsResponse response = feeds.queryFollows(request).execute().getData();
```

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

// Do I follow a list of feeds
$response = $feedsClient->queryFollows(
    new GeneratedModels\QueryFollowsRequest(
        filter: (object)[
            'source_feed' => 'timeline:john',
            'target_feed' => (object)['$in' => ['user:sara', 'user:adam']]
        ]
    )
);

echo json_encode($response->getData()->follows);

$userFeed = $feedsClient->feed('user', 'john');
$userFeed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: 'john')
);

// Paginating through followers for a feed
$firstPage = $feedsClient->queryFollows(
    new GeneratedModels\QueryFollowsRequest(
        filter: (object)['target_feed' => 'user:john'],
        limit: 20
    )
);

// Next page
$secondPage = $feedsClient->queryFollows(
    new GeneratedModels\QueryFollowsRequest(
        filter: (object)['target_feed' => 'user:john'],
        limit: 20,
        next: $firstPage->getData()->next
    )
);

// Filter by source - feeds that I follow
$sourceFollows = $feedsClient->queryFollows(
    new GeneratedModels\QueryFollowsRequest(
        filter: (object)['source_feed' => 'timeline:john'],
        limit: 20
    )
);
```

```csharp label="C#"
// Query follows
var response = await _feedsV3Client.QueryFollowsAsync(
    new QueryFollowsRequest
    {
        Limit = 10
    }
);
```

```python label="Python"
# Query follows
response = self.client.feeds.query_follows(
    limit=10,
    filter={
        "source_feed": f"{self.USER_FEED_TYPE}:{self.test_user_id}"
    }
)
```

```ruby label="Ruby"
# Query follows
# First page - paginating through followers for a feed
first_page = client.feeds.query_follows(
  GetStream::Generated::Models::QueryFollowsRequest.new(
    filter: { target_feed: 'user:john' },
    limit: 20
  )
)

# Second page using next cursor from first page
if first_page.next
  second_page = client.feeds.query_follows(
    GetStream::Generated::Models::QueryFollowsRequest.new(
      filter: { target_feed: 'user:john' },
      limit: 20,
      next: first_page.next
    )
  )
end

# Filter by source - feeds that I follow
source_follows = client.feeds.query_follows(
  GetStream::Generated::Models::QueryFollowsRequest.new(
    filter: { source_feed: 'timeline:john' },
    limit: 20
  )
)
```

</Tabs>

### Follows Queryable Built-In Fields

| name          | type                                              | description                                  | supported operations                | example                                                       |
| ------------- | ------------------------------------------------- | -------------------------------------------- | ----------------------------------- | ------------------------------------------------------------- |
| `source_feed` | string or list of strings                         | The feed ID that is following                | `$in`, `$eq`                        | `{ source_feed: { $eq: 'messaging:general' } }`               |
| `target_feed` | string or list of strings                         | The feed ID being followed                   | `$in`, `$eq`                        | `{ target_feed: { $in: [ 'sports:news', 'tech:updates' ] } }` |
| `status`      | string or list of strings                         | The follow status                            | `$in`, `$eq`                        | `{ status: { $in: [ 'accepted', 'pending', 'rejected' ] } }`  |
| `created_at`  | string, must be formatted as an RFC3339 timestamp | The time the follow relationship was created | `$eq`, `$gt`, `$gte`, `$lt`, `$lte` | `{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }`         |

## Follow Requests

Some apps require the user's approval for following them.

<Tabs>

```swift label="Swift"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
let saraFeed = saraClient.feed(
    for: .init(
        group: "user",
        id: "sara",
        data: .init(visibility: .followers)
    )
)
try await saraFeed.getOrCreate()

// Adam requesting to follow the feed
let adamTimeline = adamClient.feed(group: "timeline", id: "adam")
try await adamTimeline.getOrCreate()

let followRequest = try await adamTimeline.follow(saraFeed.feed) // user:sara
print(followRequest.status) // .pending

// Sara accepting
try await saraFeed.acceptFollow(
    adamTimeline.feed, // timeline:adam
    role: "feed_member" // optional
)
// or rejecting the request
try await saraFeed.rejectFollow(adamTimeline.feed) // timeline:adam
```

```kotlin label="Kotlin"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
val saraFeed = saraClient.feed(
    query = FeedQuery(
        group = "user",
        id = "sara",
        data = FeedInputData(visibility = FeedVisibility.Followers)
    )
)
saraFeed.getOrCreate()

// Adam requesting to follow the feed
val adamTimeline = adamClient.feed(group = "timeline", id = "adam")
adamTimeline.getOrCreate()

val followRequest = adamTimeline.follow(saraFeed.fid) // user:sara
println(followRequest.getOrNull()?.status) // FollowStatus.Pending

// Sara accepting
saraFeed.acceptFollow(
    sourceFid = adamTimeline.fid, // timeline:adam
    role = "feed_member" // optional
)
// or rejecting the request
saraFeed.rejectFollow(adamTimeline.fid) // timeline:adam

```

```js label="JavaScript"
const saraFeed = saraClient.feed("user", uuidv4());
await saraFeed.getOrCreate({
  // You need to set followers visibility to have follow requests
  data: { visibility: "followers" },
});

const adamTimeline = adamClient.feed("timeline", uuidv4());
await adamTimeline.getOrCreate();

const followRequest = await adamTimeline.follow(saraFeed.feed);

console.log(followRequest.follow.status); // pending

await saraClient.acceptFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
  // Optionally provide role
  follower_role: "feed_member",
});

await saraClient.rejectFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
});
```

```js label="React"
const saraFeed = saraClient.feed("user", uuidv4());
await saraFeed.getOrCreate({
  // You need to set followers visibility to have follow requests
  data: { visibility: "followers" },
});

const adamTimeline = adamClient.feed("timeline", uuidv4());
await adamTimeline.getOrCreate();

const followRequest = await adamTimeline.follow(saraFeed.feed);

console.log(followRequest.follow.status); // pending

await saraClient.acceptFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
  // Optionally provide role
  follower_role: "feed_member",
});

await saraClient.rejectFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
});
```

```js label="React Native"
const saraFeed = saraClient.feed("user", uuidv4());
await saraFeed.getOrCreate({
  // You need to set followers visibility to have follow requests
  data: { visibility: "followers" },
});

const adamTimeline = adamClient.feed("timeline", uuidv4());
await adamTimeline.getOrCreate();

const followRequest = await adamTimeline.follow(saraFeed.feed);

console.log(followRequest.follow.status); // pending

await saraClient.acceptFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
  // Optionally provide role
  follower_role: "feed_member",
});

await saraClient.rejectFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
});
```

```dart label="Dart"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
const saraFeedQuery = FeedQuery(
  fid: FeedId(group: 'user', id: 'sara'),
  data: FeedInputData(visibility: FeedVisibility.followers),
);
final saraFeed = saraClient.feedFromQuery(saraFeedQuery);
await saraFeed.getOrCreate();

// Adam requesting to follow the feed
final adamTimeline = adamClient.feed(group: 'timeline', id: 'adam');
await adamTimeline.getOrCreate();
final followRequest =
    await adamTimeline.follow(targetFid: saraFeed.fid); // user:sara
print(followRequest.getOrNull()?.status); // .pending
// Sara accepting
await saraFeed.acceptFollow(
  sourceFid: adamTimeline.fid, // timeline:adam
  role: 'feed_member', // optional
);
// or rejecting the request
await saraFeed.rejectFollow(sourceFid: adamTimeline.fid); // timeline:adam
```

```js label="Node"
const saraFeed = client.feeds.feed("user", uuidv4());
await saraFeed.getOrCreate({
  // You need to set followers visibility to have follow requests
  data: { visibility: "followers" },
  user_id: "sara",
});

const adamTimeline = client.feeds.feed("timeline", uuidv4());
await adamTimeline.getOrCreate({
  user_id: "adam",
});

const followRequest = await client.feeds.follow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
});

console.log(followRequest.follow.status); // pending

await client.feeds.acceptFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
  // Optionally provide role
  follower_role: "feed_member",
});

await client.feeds.rejectFollow({
  source: adamTimeline.feed,
  target: saraFeed.feed,
});
```

```go label="Go"
ctx := context.Background()

saraFeed := client.Feeds().Feed("user", "sara")
_, err = saraFeed.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  Data: &getstream.FeedInput{
    Visibility: getstream.PtrTo("followers"),
  },
  UserID: getstream.PtrTo("sara"),
})
if err != nil {
  log.Fatal("Error creating sara feed:", err)
}

adamTimeline := client.Feeds().Feed("timeline", "adam")
_, err = adamTimeline.GetOrCreate(ctx, &getstream.GetOrCreateFeedRequest{
  UserID: getstream.PtrTo("adam"),
})
if err != nil {
  log.Fatal("Error creating adam timeline feed:", err)
}

// Create follow request from adamTimeline to saraFeed
followRequest, err := client.Feeds().Follow(ctx, &getstream.FollowRequest{
  Source: "timeline:adam",
  Target: "user:sara",
})
if err != nil {
  log.Fatal("Error creating follow request:", err)
}
fmt.Printf("Follow request status: %s\n", followRequest.Data.Follow.Status)

// Accept follow request and set follower's role
_, err = client.Feeds().AcceptFollow(ctx, &getstream.AcceptFollowRequest{
  Source:       "timeline:adam",
  Target:       "user:sara",
  FollowerRole: getstream.PtrTo("feed_member"),
})
if err != nil {
  log.Fatal("Error accepting follow request:", err)
}

// Reject follow request
_, err = client.Feeds().RejectFollow(ctx, &getstream.RejectFollowRequest{
  Source: "timeline:adam",
  Target: "user:sara",
})
if err != nil {
  log.Fatal("Error rejecting follow request:", err)
}
```

```java label="Java"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
GetOrCreateFeedRequest saraFeedRequest = GetOrCreateFeedRequest.builder()
    .userID("sara")
    .data(FeedInput.builder()
        .visibility("followers")
        .build())
    .build();
feeds.getOrCreateFeed("user", "sara", saraFeedRequest).execute();

// Adam requesting to follow the feed
GetOrCreateFeedRequest adamFeedRequest = GetOrCreateFeedRequest.builder()
    .userID("adam")
    .build();
feeds.getOrCreateFeed("timeline", "adam", adamFeedRequest).execute();

// Create follow request from adamTimeline to saraFeed
FollowRequest followRequest = FollowRequest.builder()
    .source("timeline:adam")
    .target("user:sara")
    .build();
SingleFollowResponse followResponse = feeds.follow(followRequest).execute().getData();
System.out.println(followResponse.getFollow().getStatus()); // pending

// Sara accepting
AcceptFollowRequest acceptRequest = AcceptFollowRequest.builder()
    .source("timeline:adam")
    .target("user:sara")
    .followerRole("feed_member") // optional
    .build();
feeds.acceptFollow(acceptRequest).execute();

// or rejecting the request
RejectFollowRequest rejectRequest = RejectFollowRequest.builder()
    .source("timeline:adam")
    .target("user:sara")
    .build();
feeds.rejectFollow(rejectRequest).execute();
```

```php label="php"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
$saraFeed = $feedsClient->feed('user', 'sara');
$saraFeed->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(
        userID: 'sara',
        data: new GeneratedModels\FeedInput(visibility: 'followers')
    )
);

// Adam requesting to follow the feed
$adamTimeline = $feedsClient->feed('timeline', 'adam');
$adamTimeline->getOrCreateFeed(
    new GeneratedModels\GetOrCreateFeedRequest(userID: 'adam')
);

$followRequest = $feedsClient->follow(
    new GeneratedModels\FollowRequest(
        source: 'timeline:adam',
        target: 'user:sara'
    )
);
echo $followRequest->getData()->follow->status; // pending

// Sara accepting
$acceptResponse = $feedsClient->acceptFollow(
    new GeneratedModels\AcceptFollowRequest(
        source: 'timeline:adam',
        target: 'user:sara',
        followerRole: 'feed_member' // optional
    )
);

// or rejecting the request
$rejectResponse = $feedsClient->rejectFollow(
    new GeneratedModels\RejectFollowRequest(
        source: 'timeline:adam',
        target: 'user:sara'
    )
);
```

```csharp label="C#"
// Sara needs to configure the feed with visibility = followers for enabling follow requests
await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "user",
    FeedID: "sara",
    request: new GetOrCreateFeedRequest
    {
        UserID = "sara",
        Data = new FeedInput { Visibility = "followers" }
    }
);

// Adam requesting to follow the feed
await _feedsV3Client.GetOrCreateFeedAsync(
    FeedGroupID: "timeline",
    FeedID: "adam",
    request: new GetOrCreateFeedRequest { UserID = "adam" }
);

// Create follow request from adamTimeline to saraFeed
var followResponse = await _feedsV3Client.FollowAsync(
    new FollowRequest
    {
        Source = "timeline:adam",
        Target = "user:sara"
    }
);
Console.WriteLine(followResponse.Follow.Status); // pending

// Sara accepting
await _feedsV3Client.AcceptFollowAsync(
    new AcceptFollowRequest
    {
        Source = "timeline:adam",
        Target = "user:sara",
        FollowerRole = "feed_member" // optional
    }
);

// or rejecting the request
await _feedsV3Client.RejectFollowAsync(
    new RejectFollowRequest
    {
        Source = "timeline:adam",
        Target = "user:sara"
    }
);
```

```python label="Python"
# Sara needs to configure the feed with visibility = followers for enabling follow requests
feeds.get_or_create_feed(
    feed_group="user",
    feed_id="sara",
    user_id="sara",
    data={"visibility": "followers"}
)

# Adam requesting to follow the feed
feeds.get_or_create_feed(
    feed_group="timeline",
    feed_id="adam",
    user_id="adam"
)

# Create follow request from adamTimeline to saraFeed
follow_response = feeds.follow(
    source="timeline:adam",
    target="user:sara"
)
print(follow_response["follow"]["status"])  # pending

# Sara accepting
feeds.accept_follow(
    source="timeline:adam",
    target="user:sara",
    follower_role="feed_member"  # optional
)

# or rejecting the request
feeds.reject_follow(
    source="timeline:adam",
    target="user:sara"
)
```

```ruby label="Ruby"
# Sara needs to configure the feed with visibility = followers for enabling follow requests
client.feeds.get_or_create_feed(
  "user",
  "sara",
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: "sara",
    data: { "visibility" => "followers" }
  )
)

# Adam requesting to follow the feed
client.feeds.get_or_create_feed(
  "timeline",
  "adam",
  GetStream::Generated::Models::GetOrCreateFeedRequest.new(
    user_id: "adam"
  )
)

# Create follow request from adamTimeline to saraFeed
follow_response = client.feeds.follow(
  GetStream::Generated::Models::FollowRequest.new(
    source: "timeline:adam",
    target: "user:sara"
  )
)
puts follow_response.data.follow.status  # pending

# Sara accepting
client.feeds.accept_follow(
  GetStream::Generated::Models::AcceptFollowRequest.new(
    source: "timeline:adam",
    target: "user:sara",
    follower_role: "feed_member"  # optional
  )
)

# or rejecting the request
client.feeds.reject_follow(
  GetStream::Generated::Models::RejectFollowRequest.new(
    source: "timeline:adam",
    target: "user:sara"
  )
)
```

</Tabs>

## Push Preferences on Follow

Understanding the difference between `push_preference`, `skip_push` and `create_notification_activity`:

When following a feed, you can set `push_preference` to control push notifications for future activities from that feed:

- **`all`** - Receive push notifications for all activities from the followed feed
- **`none`** (default) - Don't receive push notifications for activities from the followed feed

The `skip_push` controls whether the follow action itself triggers a notification.

The `create_notification_activity` controls whether the follow action creates an activity on the source feed author's notification feed.

**Note**: You usually don't want to set `skip_push` and `create_notification_activity` true at the same time, for more information [see the Push Overview page](/activity-feeds/docs/android/push_introduction/#direct-push-notifications)

```js
// Scenario 1: Follow a user and receive notifications for their future activities
await timeline.follow("user:alice", {
  push_preference: "all", // You'll get push notifications for Alice's future posts
});

// Scenario 2: Follow a user but don't get notifications for their activities
await timeline.follow("user:bob", {
  push_preference: "none", // You won't get push notifications for Bob's future posts
});

// Scenario 3: Follow a user silently
await timeline.follow("user:charlie", {
  skip_push: true, // Charlie won't get a "you have a new follower" notification
  push_preference: "all", // But you'll still get notifications for Charlie's future posts
});

// Scenario 4: Silent follow with no future notifications
await timeline.follow("user:diana", {
  skip_push: true, // Diana won't know you followed her
  push_preference: "none", // And you won't get notifications for her posts
});

// Scenario 5: Follow a user and create notification activity for Charile
await timeline.follow("user:charlie", {
  skip_push: true, // Charlie won't get a "you have a new follower" notification
  create_notification_activity: true, // Charlie's notification feed will have a new activity
  push_preference: "all", // But you'll still get notifications for Charlie's future posts
});
```

## Built-in fields of follows

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

## Follow Suggestions

Stream provides intelligent follow suggestions to help users discover feeds they might want to follow based on their activity and social graph.

**Note:** The maximum limit for follow suggestions is 50. If a higher limit is requested, it will be automatically capped at 50.

<Tabs>

```swift label="Swift"
// Get follow suggestions for a user
let suggestions = try await client.getFollowSuggestions(
    feedGroupId: "user",
    limit: 10,
    userId: "john"
)

for suggestion in suggestions.suggestions {
    print("Suggested feed: \(suggestion.fid)")
    print("Recommendation score: \(suggestion.recommendationScore)")
    print("Reason: \(suggestion.reason)")
}
```

```kotlin label="Kotlin"
// Get follow suggestions for a user
val feed = client.feed(group = "user", id = "john")
val suggestions = feed.queryFollowSuggestions(limit = 10)

suggestions.getOrThrow().forEach { suggestion ->
    println("Suggested feed: ${suggestion.feed.fid}")
    println("Recommendation score: ${suggestion.recommendationScore}")
    println("Reason: ${suggestion.reason}")
}
```

```js label="JavaScript"
// Get follow suggestions for a user
const suggestions = await client.getFollowSuggestions({
  feed_group_id: "user",
  limit: 10,
});

console.log("Algorithm used:", suggestions.algorithm_used);
console.log("Duration:", suggestions.duration);

suggestions.suggestions.forEach((suggestion) => {
  console.log("Suggested feed:", suggestion.feed);
  console.log("Name:", suggestion.name);
  console.log("Description:", suggestion.description);
  console.log("Follower count:", suggestion.follower_count);
  console.log("Recommendation score:", suggestion.recommendation_score);
  console.log("Reason:", suggestion.reason);
  console.log("Algorithm scores:", suggestion.algorithm_scores);
});
```

```js label="React"
// Get follow suggestions for a user
const suggestions = await client.getFollowSuggestions({
  feed_group_id: "user",
  limit: 10,
});

console.log("Algorithm used:", suggestions.algorithm_used);
console.log("Duration:", suggestions.duration);

suggestions.suggestions.forEach((suggestion) => {
  console.log("Suggested feed:", suggestion.feed);
  console.log("Name:", suggestion.name);
  console.log("Description:", suggestion.description);
  console.log("Follower count:", suggestion.follower_count);
  console.log("Recommendation score:", suggestion.recommendation_score);
  console.log("Reason:", suggestion.reason);
  console.log("Algorithm scores:", suggestion.algorithm_scores);
});
```

```js label="React Native"
// Get follow suggestions for a user
const suggestions = await client.getFollowSuggestions({
  feed_group_id: "user",
  limit: 10,
});

console.log("Algorithm used:", suggestions.algorithm_used);
console.log("Duration:", suggestions.duration);

suggestions.suggestions.forEach((suggestion) => {
  console.log("Suggested feed:", suggestion.feed);
  console.log("Name:", suggestion.name);
  console.log("Description:", suggestion.description);
  console.log("Follower count:", suggestion.follower_count);
  console.log("Recommendation score:", suggestion.recommendation_score);
  console.log("Reason:", suggestion.reason);
  console.log("Algorithm scores:", suggestion.algorithm_scores);
});
```

```dart label="Dart"
// Get follow suggestions for a user
final suggestions = await client.getFollowSuggestions(
  feedGroupId: 'user',
  limit: 10,
  userId: 'john',
);

print('Algorithm used: ${suggestions.algorithmUsed}');
print('Duration: ${suggestions.duration}');

for (final suggestion in suggestions.suggestions) {
  print('Suggested feed: ${suggestion.fid}');
  print('Name: ${suggestion.name}');
  print('Description: ${suggestion.description}');
  print('Follower count: ${suggestion.followerCount}');
  print('Recommendation score: ${suggestion.recommendationScore}');
  print('Reason: ${suggestion.reason}');
  print('Algorithm scores: ${suggestion.algorithmScores}');
}
```

```js label="Node"
// Get follow suggestions for a user
const suggestions = await client.feeds.getFollowSuggestions({
  feed_group_id: "user",
  limit: 10,
  user_id: "john",
});

console.log("Algorithm used:", suggestions.algorithm_used);
console.log("Duration:", suggestions.duration);

suggestions.suggestions.forEach((suggestion) => {
  console.log("Suggested feed:", suggestion.feed);
  console.log("Name:", suggestion.name);
  console.log("Description:", suggestion.description);
  console.log("Follower count:", suggestion.follower_count);
  console.log("Recommendation score:", suggestion.recommendation_score);
  console.log("Reason:", suggestion.reason);
  console.log("Algorithm scores:", suggestion.algorithm_scores);
});
```

```go label="Go"
// Get follow suggestions for a user
suggestions, err := client.Feeds().GetFollowSuggestions(context.Background(), &getstream.GetFollowSuggestionsRequest{
    FeedGroupId: "user",
    Limit:       getstream.PtrTo(10),
    UserId:      getstream.PtrTo("john"),
})
if err != nil {
    log.Fatal("Error getting follow suggestions:", err)
}

fmt.Printf("Algorithm used: %s\n", suggestions.Data.AlgorithmUsed)
fmt.Printf("Duration: %s\n", suggestions.Data.Duration)

for _, suggestion := range suggestions.Data.Suggestions {
    fmt.Printf("Suggested feed: %s\n", suggestion.Fid)
    fmt.Printf("Name: %s\n", suggestion.Name)
    fmt.Printf("Description: %s\n", suggestion.Description)
    fmt.Printf("Follower count: %d\n", suggestion.FollowerCount)
    fmt.Printf("Recommendation score: %.2f\n", suggestion.RecommendationScore)
    fmt.Printf("Reason: %s\n", suggestion.Reason)
    fmt.Printf("Algorithm scores: %+v\n", suggestion.AlgorithmScores)
}
```

```java label="Java"
// Get follow suggestions for a user
GetFollowSuggestionsRequest request = GetFollowSuggestionsRequest.builder()
    .feedGroupId("user")
    .limit(10)
    .userId("john")
    .build();

GetFollowSuggestionsResponse response = feeds.getFollowSuggestions(request).execute().getData();

System.out.println("Algorithm used: " + response.getAlgorithmUsed());
System.out.println("Duration: " + response.getDuration());

for (FollowSuggestion suggestion : response.getSuggestions()) {
    System.out.println("Suggested feed: " + suggestion.getFid());
    System.out.println("Name: " + suggestion.getName());
    System.out.println("Description: " + suggestion.getDescription());
    System.out.println("Follower count: " + suggestion.getFollowerCount());
    System.out.println("Recommendation score: " + suggestion.getRecommendationScore());
    System.out.println("Reason: " + suggestion.getReason());
    System.out.println("Algorithm scores: " + suggestion.getAlgorithmScores());
}
```

```php label="PHP"
// Get follow suggestions for a user
$suggestions = $feedsClient->getFollowSuggestions(
    new GeneratedModels\GetFollowSuggestionsRequest(
        feedGroupId: 'user',
        limit: 10,
        userId: 'john'
    )
);

echo "Algorithm used: " . $suggestions->getData()->algorithmUsed . "\n";
echo "Duration: " . $suggestions->getData()->duration . "\n";

foreach ($suggestions->getData()->suggestions as $suggestion) {
    echo "Suggested feed: " . $suggestion->fid . "\n";
    echo "Name: " . $suggestion->name . "\n";
    echo "Description: " . $suggestion->description . "\n";
    echo "Follower count: " . $suggestion->followerCount . "\n";
    echo "Recommendation score: " . $suggestion->recommendationScore . "\n";
    echo "Reason: " . $suggestion->reason . "\n";
    echo "Algorithm scores: " . json_encode($suggestion->algorithmScores) . "\n";
}
```

```csharp label="C#"
// Get follow suggestions for a user
var response = await _feedsV3Client.GetFollowSuggestionsAsync(
    new GetFollowSuggestionsRequest
    {
        FeedGroupId = "user",
        Limit = 10,
        UserId = "john"
    }
);

Console.WriteLine($"Algorithm used: {response.AlgorithmUsed}");
Console.WriteLine($"Duration: {response.Duration}");

foreach (var suggestion in response.Suggestions)
{
    Console.WriteLine($"Suggested feed: {suggestion.Fid}");
    Console.WriteLine($"Name: {suggestion.Name}");
    Console.WriteLine($"Description: {suggestion.Description}");
    Console.WriteLine($"Follower count: {suggestion.FollowerCount}");
    Console.WriteLine($"Recommendation score: {suggestion.RecommendationScore}");
    Console.WriteLine($"Reason: {suggestion.Reason}");
    Console.WriteLine($"Algorithm scores: {JsonSerializer.Serialize(suggestion.AlgorithmScores)}");
}
```

```python label="Python"
# Get follow suggestions for a user
suggestions = client.feeds.get_follow_suggestions(
    feed_group_id="user",
    limit=10,
    user_id="john"
)

print(f"Algorithm used: {suggestions.algorithm_used}")
print(f"Duration: {suggestions.duration}")

for suggestion in suggestions.suggestions:
    print(f"Suggested feed: {suggestion.fid}")
    print(f"Name: {suggestion.name}")
    print(f"Description: {suggestion.description}")
    print(f"Follower count: {suggestion.follower_count}")
    print(f"Recommendation score: {suggestion.recommendation_score}")
    print(f"Reason: {suggestion.reason}")
    print(f"Algorithm scores: {suggestion.algorithm_scores}")
```

```ruby label="Ruby"
# Get follow suggestions for a user
suggestions = client.feeds.get_follow_suggestions(
  feed_group_id: 'user',
  limit: 10,
  user_id: 'john'
)

puts "Algorithm used: #{suggestions.algorithm_used}"
puts "Duration: #{suggestions.duration}"

suggestions.suggestions.each do |suggestion|
  puts "Suggested feed: #{suggestion.fid}"
  puts "Name: #{suggestion.name}"
  puts "Description: #{suggestion.description}"
  puts "Follower count: #{suggestion.follower_count}"
  puts "Recommendation score: #{suggestion.recommendation_score}"
  puts "Reason: #{suggestion.reason}"
  puts "Algorithm scores: #{suggestion.algorithm_scores}"
end
```

</Tabs>

### Response Fields

The follow suggestions response includes:

- **`suggestions`**: Array of suggested feeds to follow
  - `feed`: Feed identifier
  - `name`: Feed name
  - `description`: Feed description
  - `visibility`: Feed visibility setting
  - `member_count`: Number of members
  - `follower_count`: Number of followers
  - `following_count`: Number of feeds this feed follows
  - `created_at`: When the feed was created
  - `updated_at`: When the feed was last updated
  - `recommendation_score`: Combined recommendation score (0-1)
  - `reason`: Human-readable reason for the suggestion
  - `algorithm_scores`: Individual algorithm scores
- **`algorithm_used`**: The algorithm used to generate suggestions
- **`duration`**: Request processing time

### Algorithm Types

Stream's follow suggestions use a sophisticated multi-algorithm approach with weighted scoring:

- **`popularity`** (Weight: 0.3): Based on follower count and engagement
  - Calculates normalized follower count relative to the most popular feed in your app
  - Score = min(follower_count / max_follower_count, 1.0)
  - Helps surface trending and popular content

- **`friend-of-friend`** (Weight: 0.7): Based on social connections and mutual follows
  - Analyzes how many of your followed feeds also follow the suggested feed
  - Score = mutual_follows / your_total_follows
  - Leverages social proof and network effects

- **`combined`**: Uses multiple algorithms with weighted scoring
  - Final score = (popularity_score × 0.3 + friend_of_friend_score × 0.7) / total_weight
  - Provides balanced recommendations combining popularity and social relevance

**Note:** Additional algorithms will be added in future releases to provide even more sophisticated recommendations.

### Scoring System

The recommendation system uses a sophisticated scoring mechanism:

1. **Individual Algorithm Scores**: Each algorithm calculates a score from 0.0 to 1.0
2. **Weighted Combination**: Scores are combined using configurable weights
3. **Normalization**: Final scores are normalized to ensure fair comparison
4. **Filtering**: Only feeds with positive combined scores are included
5. **Ranking**: Results are sorted by combined score in descending order

### Features

- Excludes feeds already followed by the user
- Excludes user's own feeds
- Sophisticated algorithm to find feeds to follow

## Batch follow & unfollow

`getOrCreateFollows`/`getOrCreateUnfollows` endpoints allow creating a maximum of 100 follow/unfollow at once.

These are idempotent endpoints (as opposed to `follow` and `unfollow`), trying to follow/unfollow a feed that's already/not yet followed won't cause errors.

<Tabs>

```kotlin label="Kotlin"
// Batch follow
val requests = listOf(
  FollowRequest(
    source = timeline.fid.rawValue,
    target = fid1.rawValue,
  ),
  FollowRequest(
    source = timeline.fid.rawValue,
    target = fid2.rawValue,
  ),
)
client.getOrCreateFollows(FollowBatchRequest(requests))
  .onSuccess { batchData ->
    println("Created follows: ${batchData.created}")
    println("Follows: ${batchData.follows}")
  }

// Batch unfollow
val unfollowPairs = listOf(
  UnfollowPair(
    source = timeline.fid.rawValue,
    target = fid1.rawValue,
  ),
  UnfollowPair(
    source = timeline.fid.rawValue,
    target = fid2.rawValue,
  ),
)
client.getOrCreateUnfollows(UnfollowBatchRequest(unfollowPairs))
  .onSuccess { follows ->
    println("Follows that were removed: $follows")
  }
```

```js label="JavaScript"
const response = await client.getOrCreateFollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
      // Optional
      push_preference: "all",
      custom: {
        reason: "investment",
      },
    },
    {
      source: timeline.feed,
      target: feed2.feed,
    },
  ],
});

console.log("Created follows:", response.created);
console.log("Follows:", response.follows);

await client.getOrCreateUnfollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
    },
  ],
});

console.log("Follows that were removed:", response.follows);
```

```js label="React"
const response = await client.getOrCreateFollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
      // Optional
      push_preference: "all",
      custom: {
        reason: "investment",
      },
    },
    {
      source: timeline.feed,
      target: feed2.feed,
    },
  ],
});

console.log("Created follows:", response.created);
console.log("Follows:", response.follows);

await client.getOrCreateUnfollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
    },
  ],
});

console.log("Follows that were removed:", response.follows);
```

```js label="React Native"
const response = await client.getOrCreateFollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
      // Optional
      push_preference: "all",
      custom: {
        reason: "investment",
      },
    },
    {
      source: timeline.feed,
      target: feed2.feed,
    },
  ],
});

console.log("Created follows:", response.created);
console.log("Follows:", response.follows);

await client.getOrCreateUnfollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
    },
  ],
});

console.log("Follows that were removed:", response.follows);
```

```js label="Node"
const response = await client.feeds.getOrCreateFollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
      // Optional
      push_preference: "all",
      custom: {
        reason: "investment",
      },
    },
    {
      source: timeline.feed,
      target: feed2.feed,
    },
  ],
});

console.log("Created follows:", response.created);
console.log("Follows:", response.follows);

await client.feeds.getOrCreateUnfollows({
  follows: [
    {
      source: timeline.feed,
      target: feed.feed,
    },
  ],
});

console.log("Follows that were removed:", response.follows);
```

```go label="Go"
ctx := context.Background()

// Batch create follows
response, err := client.Feeds().GetOrCreateFollows(ctx, &getstream.GetOrCreateFollowsRequest{
  Follows: []getstream.FollowRequest{
    {
      Source: "timeline:john",
      Target: "user:tom",
      // Optional
      PushPreference: getstream.PtrTo("all"),
      Custom: map[string]any{
        "reason": "investment",
      },
    },
    {
      Source: "timeline:john",
      Target: "stock:apple",
    },
  },
})
if err != nil {
  log.Fatal("Error creating follows:", err)
}

fmt.Printf("Created follows: %d\n", len(response.Data.Created))
fmt.Printf("Total follows: %d\n", len(response.Data.Follows))

// Batch remove follows
unfollowResponse, err := client.Feeds().GetOrCreateUnfollows(ctx, &getstream.GetOrCreateUnfollowsRequest{
  Follows: []getstream.FollowPair{
    {
      Source: "timeline:john",
      Target: "user:tom",
    },
  },
})
if err != nil {
  log.Fatal("Error removing follows:", err)
}

fmt.Printf("Follows that were removed: %d\n", len(unfollowResponse.Data.Follows))
```

```python label="Python"
# Batch create follows
response = client.feeds.get_or_create_follows(
    follows=[
        {
            "source": "timeline:john",
            "target": "user:tom",
            # Optional
            "push_preference": "all",
            "custom": {
                "reason": "investment"
            }
        },
        {
            "source": "timeline:john",
            "target": "stock:apple"
        }
    ]
)

print(f"Created follows: {len(response['created'])}")
print(f"Total follows: {len(response['follows'])}")

# Batch remove follows
unfollow_response = client.feeds.get_or_create_unfollows(
    follows=[
        {
            "source": "timeline:john",
            "target": "user:tom"
        }
    ]
)

print(f"Follows that were removed: {len(unfollow_response['follows'])}")
```

```php label="php"
// Batch create follows
$response = $feedsClient->getOrCreateFollows(
    new GeneratedModels\GetOrCreateFollowsRequest(
        follows: [
            new GeneratedModels\FollowInput(
                source: 'timeline:john',
                target: 'user:tom',
                // Optional
                pushPreference: 'all',
                custom: (object)['reason' => 'investment']
            ),
            new GeneratedModels\FollowInput(
                source: 'timeline:john',
                target: 'stock:apple'
            )
        ]
    )
);

echo "Created follows: " . count($response->getData()->created) . "\n";
echo "Total follows: " . count($response->getData()->follows) . "\n";

// Batch remove follows
$unfollowResponse = $feedsClient->getOrCreateUnfollows(
    new GeneratedModels\GetOrCreateUnfollowsRequest(
        follows: [
            new GeneratedModels\FollowInput(
                source: 'timeline:john',
                target: 'user:tom'
            )
        ]
    )
);

echo "Follows that were removed: " . count($unfollowResponse->getData()->follows) . "\n";
```

```java label="Java"
// Batch create follows
GetOrCreateFollowsRequest followsRequest = GetOrCreateFollowsRequest.builder()
    .follows(Arrays.asList(
        FollowInput.builder()
            .source("timeline:john")
            .target("user:tom")
            // Optional
            .pushPreference("all")
            .custom(Map.of("reason", "investment"))
            .build(),
        FollowInput.builder()
            .source("timeline:john")
            .target("stock:apple")
            .build()
    ))
    .build();

GetOrCreateFollowsResponse response = feeds.getOrCreateFollows(followsRequest).execute().getData();

System.out.println("Created follows: " + response.getCreated().size());
System.out.println("Total follows: " + response.getFollows().size());

// Batch remove follows
GetOrCreateUnfollowsRequest unfollowsRequest = GetOrCreateUnfollowsRequest.builder()
    .follows(Arrays.asList(
        FollowInput.builder()
            .source("timeline:john")
            .target("user:tom")
            .build()
    ))
    .build();

GetOrCreateUnfollowsResponse unfollowResponse = feeds.getOrCreateUnfollows(unfollowsRequest).execute().getData();

System.out.println("Follows that were removed: " + unfollowResponse.getFollows().size());
```

```csharp label="C#"
// Batch create follows
var response = await _feedsV3Client.GetOrCreateFollowsAsync(
    new GetOrCreateFollowsRequest
    {
        Follows = new List<FollowInput>
        {
            new FollowInput
            {
                Source = "timeline:john",
                Target = "user:tom",
                // Optional
                PushPreference = "all",
                Custom = new Dictionary<string, object>
                {
                    { "reason", "investment" }
                }
            },
            new FollowInput
            {
                Source = "timeline:john",
                Target = "stock:apple"
            }
        }
    }
);

Console.WriteLine($"Created follows: {response.Created.Count}");
Console.WriteLine($"Total follows: {response.Follows.Count}");

// Batch remove follows
var unfollowResponse = await _feedsV3Client.GetOrCreateUnfollowsAsync(
    new GetOrCreateUnfollowsRequest
    {
        Follows = new List<FollowInput>
        {
            new FollowInput
            {
                Source = "timeline:john",
                Target = "user:tom"
            }
        }
    }
);

Console.WriteLine($"Follows that were removed: {unfollowResponse.Follows.Count}");
```

```ruby label="Ruby"
# Batch create follows
response = client.feeds.get_or_create_follows(
  GetStream::Generated::Models::GetOrCreateFollowsRequest.new(
    follows: [
      GetStream::Generated::Models::FollowInput.new(
        source: 'timeline:john',
        target: 'user:tom',
        # Optional
        push_preference: 'all',
        custom: { 'reason' => 'investment' }
      ),
      GetStream::Generated::Models::FollowInput.new(
        source: 'timeline:john',
        target: 'stock:apple'
      )
    ]
  )
)

puts "Created follows: #{response.data.created.length}"
puts "Total follows: #{response.data.follows.length}"

# Batch remove follows
unfollow_response = client.feeds.get_or_create_unfollows(
  GetStream::Generated::Models::GetOrCreateUnfollowsRequest.new(
    follows: [
      GetStream::Generated::Models::FollowInput.new(
        source: 'timeline:john',
        target: 'user:tom'
      )
    ]
  )
)

puts "Follows that were removed: #{unfollow_response.data.follows.length}"
```

</Tabs>


---

This page was last updated at 2026-05-08T12:56:48.461Z.

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