# Batch Methods

## Batch Add Activities

Multiple activities can be added with a single batch operation. This is very convenient when importing data to Stream.

<Tabs>

```csharp label="C#"
var activities = new[]
{
  new Activity("User:1", "tweet", "Tweet:1"),
  new Activity("User:2", "watch", "Movie:1")
};

await userFeed1.AddActivitiesAsync(activities);
```

```js label="JavaScript"
const activities = [
  { actor: "User:1", verb: "tweet", object: "Tweet:1" },
  { actor: "User:2", verb: "watch", object: "Movie:1" },
];

const addActivities = await user1.addActivities(activities);
```

```ruby label="Ruby"
activities = [
  {:actor => 'User:1', :verb => 'tweet', :object => 'Tweet:1'},
  {:actor => 'User:2', :verb => 'watch', :object => 'Movie:1'}
]
user_feed_1.add_activities(activities)
```

```python label="Python"
activities = [
  {'actor': 'User:1', 'verb': 'tweet', 'object': 'Tweet:1'},
  {'actor': 'User:2', 'verb': 'watch', 'object': 'Movie:1'}
]
user_feed_1.add_activities(activities)
```

```java label="Java"
Activity[] activities = new Activity[]{
    Activity.builder()
        .actor("User:1")
        .verb("tweet")
        .object("Tweet:1")
        .build(),
    Activity.builder()
        .actor("User:2")
        .verb("watch")
        .object("Movie:1")
        .build()
};
userFeed.addActivities(activities).join();
```

```go label="Go"
activities := []stream.Activity{
		{Actor: "user:42", Verb: "tweet", Object: "Tweet:1"},
		{Actor: "user:42", Verb: "watch", Object: "Movie:1"},
	}
	_, err := userFeed.AddActivities(context.TODO(), activities...)
	if err != nil {
		panic(err)
	}
```

```php label="PHP"
$activities = [
  ['actor' => 'User:1', 'verb' => 'tweet', 'object' => 'Tweet:1'],
  ['actor' => 'User:2', 'verb' => 'watch', 'object' => 'Movie:1'],
];

$userFeed1->addActivities($activities);
```

</Tabs>

### Parameters

| name       | type | description                                                                                                               | default | optional |
| ---------- | ---- | ------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| activities | list | The list of activities to be added (as specified in [Adding Activities](/activity-feeds/docs/node/v2/adding-activities/)) | -       | ✓        |

### Activity IDs

The API will return a response with a list containing the activity ids.

<admonition type="info">

If you are importing your data for the first time, we suggest you create the follow-relationships after the activity import.

</admonition>

## Batch Activity Add

This method allows you to add a single activity to multiple feeds with one API request.

<admonition type="info">

Batch Activity Add has a limit of 5000 target feeds. Requests that exceed this limit will return an error.

</admonition>

<admonition type="info">

`TO` field [targeting](/activity-feeds/docs/node/v2/targeting/) is not permitted via this endpoint. Attempting to add activities with a 'to' property will result in an error response.

</admonition>

<Tabs>

```csharp label="C#"
// adds 1 activity to many feeds in one request
var feeds = new[] { "timeline:1", "timeline:2", "timeline:3", "timeline:4" };
var activity = new Activity("User:2", "pin", "Place:42")
{
  Target = "Board:1"
};
await client.Batch.AddToManyAsync(activity, feeds);
```

```js label="JavaScript"
const feeds = ["timeline:1", "timeline:2", "timeline:3", "timeline:4"];
const activity = {
  actor: "User:2",
  verb: "pin",
  object: "Place:42",
  target: "Board:1",
};
const response = await client.addToMany(activity, feeds);
```

```python label="Python"
# adds 1 activity to many feeds in one request
feeds = ['timeline:1', 'timeline:2', 'timeline:3', 'timeline:4']
activity = {"actor": "User:2", "verb": "pin", "object": "Place:42", "target": "Board:1"}
client.add_to_many(activity, feeds)
```

```ruby label="Ruby"
# adds 1 activity to many feeds in one request
feeds = ['timeline:1', 'timeline:2', 'timeline:3', 'timeline:4']
activity = {:actor =&gt; "User:2", :verb =&gt; "pin", :object =&gt; "Place:42", :target =&gt; "Board:1"}
client.add_to_many(activity, feeds)
```

```php label="PHP"
// adds 1 activity to many feeds in one request
$activity = [
  'actor' =&gt; 'User:2',
  'verb' =&gt; 'pin',
  'object' =&gt; 'Place:42',
  'target' =&gt; 'Board:1'
];

$feeds = ['timeline:1', 'timeline:2', 'timeline:3', 'timeline:4'];

$batcher-&gt;addToMany($activity, $feeds);
```

```java label="Java"
// adds 1 activity to many feeds in one request
activity = Activity.builder()
    .actor("User:2")
    .verb("pin")
    .object("Place:42")
    .target("Board:1")
    .build();
FeedID[] feeds = new FeedID[]{
    new FeedID("timeline", "1"),
    new FeedID("timeline", "2"),
    new FeedID("timeline", "3"),
    new FeedID("timeline", "4")
};
client.batch().addToMany(activity, feeds).join();
```

```go label="Go"
// adds 1 activity to many feeds in one request
	feeds := make([]stream.Feed, 0, 4)
	for i := 0; i < 4; i++ {
		f, err := client.FlatFeed("timeline", strconv.Itoa(i))
		if err != nil {
			panic(err)
		}
		feeds = append(feeds, f)
	}

	activity := stream.Activity{
		Actor: "user:42",
		Verb:  "pin",
		Object: "place:42",
		Target: "board:1",
	}
	err := client.AddToMany(context.TODO(), activity, feeds...)
	if err != nil {
		panic(err)
	}
```

</Tabs>

### Parameters

| name     | type   | description                                           |
| -------- | ------ | ----------------------------------------------------- |
| feeds    | list   | The list of a feeds e.g. ['user:1', 'timeline:2']     |
| activity | object | The activity object (see Feed endpoint for reference) |

<admonition type="info">

Activities added using this method are not propagated to followers. That is, any other Feeds that follow the Feed(s) listed in the API call will not receive the new Activity.

</admonition>

<admonition type="info">

Even if real-time is enabled, for this endpoint it is disabled by default since it's mainly for backend processing which shouldn't create noise for users. If you want to enable it, please send a request to support with your app id.

</admonition>

## Batch get Activities by ID

Activities can be retrieved by IDs or foreign ID and time.

<Tabs>

```csharp label="C#"
// retrieve two activities by ID
await client.Batch.GetActivitiesAsync(new[]
{
  "01b3c1dd-e7ab-4649-b5b3-b4371d8f7045",
  "ed2837a6-0a3b-4679-adc1-778a1704852d"
});

// retrieve two activities by foreign ID and timestamp
await client.Batch.GetActivitiesAsync(null, new[]
{
  new ForeignIdTime("like:1", DateTime.Parse("2018-07-08T14:09:36.000000")),
  new ForeignIdTime("post:1", DateTime.Parse("2018-07-09T20:30:40.000000"))
});
```

```js label="JavaScript"
// retrieve two activities by ID
const response = await client.getActivities({
  ids: [
    "01b3c1dd-e7ab-4649-b5b3-b4371d8f7045",
    "ed2837a6-0a3b-4679-adc1-778a1704852d",
  ],
});

// retrieve two activities by their foreign ID and time
const response = await client.getActivities({
  foreignIDTimes: [
    { foreignID: "like:1", time: "2018-07-08T14:09:36.000000" },
    { foreignID: "post:2", time: "2018-07-09T20:30:40.000000" },
  ],
});

const response = await client.getActivities({
  ids: ["1febf8dd-cbbb-11ec-9717-025b47ecba9d"],
  reactions: { recent: true, counts: true, own: true, kind: true },
});
```

```python label="Python"
# retrieve two activities by ID
client.get_activities(ids=[
  '01b3c1dd-e7ab-4649-b5b3-b4371d8f7045',
  'ed2837a6-0a3b-4679-adc1-778a1704852d'
])

# retrieve an activity by foreign ID and time
client.get_activities(foreign_id_times=[
  (foreign_id, activity_time),
])
```

```ruby label="Ruby"
# retrieve two activities by ID
client.get_activities(
 ids: [
  '01b3c1dd-e7ab-4649-b5b3-b4371d8f7045',
  'ed2837a6-0a3b-4679-adc1-778a1704852d'
 ]
)

# retrieve two activities by foreign ID and timestamp
client.get_activities(
 foreign_id_times: [
  { foreign_id: 'like:1', time: '2018-07-08T14:09:36.000000' },
  { foreign_id: 'post:2', time: '2018-07-09T20:30:40.000000' }
 ]
)
```

```php label="PHP"
# not supported yet
$client-&gt;getActivities([
  '01b3c1dd-e7ab-4649-b5b3-b4371d8f7045',
  'ed2837a6-0a3b-4679-adc1-778a1704852d'
])

# retrieve an activity by foreign ID and time (an array of arrays)
$client-&gt;getActivities(null, [[$foreign_id, $activity_time]])
```

```java label="Java"
// retrieve two activities by ID
client.batch().getActivitiesByID("01b3c1dd-e7ab-4649-b5b3-b4371d8f7045", "ed2837a6-0a3b-4679-adc1-778a1704852").join();

// retrieve two activities by foreign ID and timestamp
client.batch().getActivitiesByForeignID(new ForeignIDTimePair("foreignID1", new Date()), new ForeignIDTimePair("foreignID2", new Date())).join();
```

```go label="Go"
// retrieve two activities by ID
	resp, err := client.GetActivitiesByID(
		context.TODO(),
		"01b3c1dd-e7ab-4649-b5b3-b4371d8f7045",
		"ed2837a6-0a3b-4679-adc1-778a1704852d",
	)
	if err != nil {
		panic(err)
	}

	// retrieve two activities by foreign ID and timestamp
	resp, err := client.GetActivitiesByForeignID(
		context.TODO(),
		stream.NewForeignIDTimePair("like:1", stream.Time{Time: /* ... */}),
		stream.NewForeignIDTimePair("post:2", stream.Time{Time: /* ... */}),
	)
	if err != nil {
		panic(err)
	}
```

```js label="Node"
const response = await client.getActivities({
  ids: ["1febf8dd-cbbb-11ec-9717-025b47ecba9d"],
  reactions: { recent: true, counts: true, own: true, kind: true },
  user_id: "luis",
});
```

</Tabs>

<admonition type="info">

Combining ID and foreign ID + time parameters is not allowed.

</admonition>

### Parameters

| name             | type    | description                                                        | default | optional |
| ---------------- | ------- | ------------------------------------------------------------------ | ------- | -------- |
| ids              | string  | The comma-separated list of activity IDs to retrieve               | -       | ✓        |
| foreign_id_times | list    | The list of foreign_id and time values used to retrieve activities | -       | ✓        |
| reactions.own    | boolean | Include reactions added by current user to all activities          | -       | ✓        |
| reactions.recent | boolean | Include recent reactions to activities                             | -       | ✓        |
| reaction.counts  | boolean | Include reaction counts to activities                              | -       | ✓        |

<admonition type="info">

The number of activities that can be retrieved with a single request is limited to 100.

</admonition>

<admonition type="info">

When using this endpoint server side you must include the `user_id` parameter for the own reactions to properly populated.

</admonition>

## Batch Follow

Stream's Follow Many functionality gives you a fast method to follow many feeds in one go. This is convenient when importing data or on-boarding new users.

Follow Many has a limit of 2,500 follows per request.

<admonition type="info">

Follow Many has a total request size limit of 128kb, in case you reach it we recommended to split the request in 2.

</admonition>

`activity_copy_limit` can be specified to copy specific number of activities. By default, it's 100 such that upon follow 100 activities from the followed feed will be seen in our feed.

<Tabs>

```csharp label="C#"
// Batch following many feeds
// Let timeline:1 follow user:1, user:2 and user:3
var follows = new[]
{
  new Follow("timeline:1", "user:1"),
  new Follow("timeline:1", "user:2"),
  new Follow("timeline:1", "user:3")
};
await client.Batch.FollowManyAsync(follows);

// copy only the last 10 activities from every feed
await client.Batch.FollowManyAsync(follows, 10);
```

```js label="JavaScript"
// Batch following many feeds
// Let timeline:1 will follow user:1, user:2 and user:3

const follows = [
  { source: "timeline:1", target: "user:1" },
  { source: "timeline:1", target: "user:2" },
  { source: "timeline:1", target: "user:3", activity_copy_limit: 0 },
];

await client.followMany(follows);
```

```python label="Python"
# Batch following many feeds
# Let timeline:1 will follow user:1, user:2 and user:3
follows = [
  {'source': 'timeline:1', 'target': 'user:1'},
  {'source': 'timeline:1', 'target': 'user:2'},
  {'source': 'timeline:1', 'target': 'user:3'}
]
client.follow_many(follows)
# copy only the last 10 activities from every feed
client.follow_many(follows, activity_copy_limit=10)
```

```ruby label="Ruby"
# Batch following many feeds
# Let timeline:1 will follow user:1, user:2 and user:3
follows = [
  {:source =&gt; 'timeline:1', :target =&gt; 'user:1'},
  {:source =&gt; 'timeline:1', :target =&gt; 'user:2'},
  {:source =&gt; 'timeline:1', :target =&gt; 'user:3'}
]
client.follow_many(follows)
```

```php label="PHP"
// Batch following many feeds
// Let timeline:1 will follow user:1, user:2 and user:3
$follows = [
  ['source' =&gt; 'timeline:1', 'target' =&gt; 'user:1'],
  ['source' =&gt; 'timeline:1', 'target' =&gt; 'user:2'],
  ['source' =&gt; 'timeline:1', 'target' =&gt; 'user:3'],
];

$batcher-&gt;followMany($follows);
```

```java label="Java"
// Batch following many feeds
// Let timeline:1 will follow user:1, user:2 and user:3
FollowRelation[] follows = new FollowRelation[]{
    new FollowRelation("timeline:1", "user:1"),
    new FollowRelation("timeline:3", "user:2"),
    new FollowRelation("timeline:1", "user:3")
};
client.batch().followMany(follows).join();
// copy only the last 10 activities from every feed
client.batch().followMany(10, follows).join();
```

```go label="Go"
// Batch following many feeds
	// Let timeline:1 will follow user:1, user:2 and user:3
	follows := []stream.FollowRelationship{
		stream.NewFollowRelationship(timelineFeed, userFeed1),
		stream.NewFollowRelationship(timelineFeed, userFeed2),
		stream.NewFollowRelationship(timelineFeed, userFeed3),
	}
	err := client.FollowMany(context.TODO(), follows)
	if err != nil {
		panic(err)
	}
	// copy only the last 10 activities from every feed
	err = client.FollowMany(context.TODO(), follows, stream.WithFollowManyActivityCopyLimit(10))
	if err != nil {
		panic(err)
	}

	// Unfollow many
	unfollows := []stream.UnfollowRelationship{}
	err = client.UnfollowMany(context.TODO(), unfollows)
	if err != nil {
		panic(err)
	}
```

</Tabs>

<admonition type="info">

This method can only be used server-side.

</admonition>

<admonition type="warning">

Note that the follow relationships will be processed in the background asynchronously so they wont show up immediately.

</admonition>

Parameters

<admonition type="warning">

The Batch Follow API does not return response data.

</admonition>

## Batch Unfollow

Unfollow Many enables you to unfollow many feeds in bulk. However, its implementation is heavy. That's why its usage restricted to Enterprise accounts. While unfollowing, history can be kept by `keep_history.` It's false by default. When unfollow is done, any activities that come from unfollowed feed will be removed but if the flag is set, then this history will remain.

Unfollow Many has a limit of 250 unfollows per request.

<Tabs>

```js label="JavaScript"
// Batch unfollowing many feeds
// Let timeline:1 will follow user:1, user:2 and user:3

const unfollows = [
  { source: "timeline:1", target: "user:1" },
  { source: "timeline:1", target: "user:2" },
  { source: "timeline:1", target: "user:3", keep_history: true },
];

await client.unfollowMany(unfollows);
```

```go label="Go"
// Unfollow many
	unfollows := []stream.UnfollowRelationship{ /* ... */ }
	err := client.UnfollowMany(context.TODO(), unfollows)
	if err != nil {
		panic(err)
	}
```

</Tabs>

<admonition type="warning">

The Batch Unfollow API does not return response data. An error is returned on wrong input.

</admonition>

## Batch update TO targets

Allows you to update the TO targets of multiple activities in a feed at once, up to a max of 100 activities per call.

<Tabs>

```go label="Go"
feed, err := client.FlatFeed("user", "1")
if err != nil {
	panic(err)
}

reqs := []stream.UpdateToTargetsRequest{
	{
		ForeignID: "foreignID-0",
		Time:   stream.Time{},
		Opts: []stream.UpdateToTargetsOption{
			stream.WithToTargetsAdd("foo:bar", "baz:qux"),
		},
	},
	{
		ForeignID: "foreignID-1",
		Time:   stream.Time{},
		Opts: []stream.UpdateToTargetsOption{
			stream.WithToTargetsAdd("foo:bar", "baz:qux"),
			stream.WithToTargetsRemove("abc:123"),
		},
	},
}

resp, err := feed.BatchUpdateToTargets(context.Background(), reqs)
if err != nil {
	panic(err)
}
```

```csharp label="C#"
var feed = client.Feed("user", "1");

var reqs = new List<UpdateToTargetsRequest>
{
    new UpdateToTargetsRequest
    {
        ForeignID = "foreignID-0",
        Time = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fff"),
        Adds = new List<string> { "foo:bar", "baz:qux" }
    },
    new UpdateToTargetsRequest
    {
        ForeignID = "foreignID-1",
        Time = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fff"),
        Adds = new List<string> { "foo:bar", "baz:qux" },
        RemovedTargets = new List<string> { "abc:123" }
    }
};

try
{
    var resp = await feed.BatchUpdateActivityToTargetsAsync(reqs);

    Console.WriteLine($"Updated activity: {resp.Activity.Id}");
    Console.WriteLine($"Added targets: {string.Join(", ", resp.Added)}");
    Console.WriteLine($"Removed targets: {string.Join(", ", resp.Removed)}");
}
catch (StreamException ex)
{
    Console.WriteLine($"Stream API error: {ex.Message}");
    throw;
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
    throw;
}
```

</Tabs>

<admonition type="info">

Currently only available in the Go and .NET SDKs, if you need support for your SDK please contact support.

</admonition>


---

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

For the most recent version of this documentation, visit [https://getstream.io/activity-feeds/docs/php/v2/add-many-activities/](https://getstream.io/activity-feeds/docs/php/v2/add-many-activities/).