Activities

Creating Activities

The example below shows how to create an activity and add it to a feed.

response = self.client.feeds.add_activity(
    type="post",
    feeds=[self.test_feed.get_feed_identifier()],
    text="This is a test activity from Python SDK",
    user_id=self.test_user_id,
    custom={
        "test_field": "test_value",
        "timestamp": int(datetime.now().timestamp()),
    },
)

The above example was quite simple. Here are a few more examples:

Image & Video

response = self.client.feeds.add_activity(
    type="video",
    feeds=[self.test_feed.get_feed_identifier()],
    text="Check out this amazing video!",
    user_id=self.test_user_id,
    attachments=[
        Attachment(
            custom={"duration": 120},
            asset_url="https://example.com/amazing-video.mp4",
            type="video",
            title="Amazing Video",
        )
    ],
    custom={"video_quality": "4K", "duration_seconds": 120},
)

Sharing activities

When creating an activity it’s possible to set parent_id, setting this field will increase the share_count of the parent activity. This feature lets you implement “retweets”.

When reading an activity with parent id set, you can access the parent activity with activity.parent.

response = self.client.feeds.add_activity(
    type="post",
    feeds=[self.test_feed.get_feed_identifier()],
    text="Couldn't agree more!",
    parent_id="<activity to share>",
    user_id=self.test_user_id,
)

Activity request options

The following options can be provided when creating an activity:

AddActivityRequest
NameTypeDescriptionConstraints
attachmentsAttachment[]List of attachments for the activity-
collection_refsstring[]Collections that this activity references-
customobjectCustom data for the activity-
expires_atstringExpiration time for the activity-
feedsstring[]List of feed IDs to add the activity toRequired, Minimum: 1, Maximum: 255
filter_tagsstring[]Tags for filtering activitiesMaximum: 255
idstringOptional ID for the activity-
interest_tagsstring[]Tags for indicating user interestsMaximum: 255
locationActivityLocationGeographic location related to the activity-
mentioned_user_idsstring[]List of users mentioned in the activityMaximum: 255
parent_idstringID of parent activity for replies/comments-
poll_idstringID of a poll to attach to activity-
restrict_repliesstring (everyone, people_i_follow, nobody)Controls who can add comments/replies to this activity. Options: 'everyone' (default - anyone can reply), 'people_i_follow' (only people the activity creator follows can reply), 'nobody' (no one can reply)-
search_dataobjectAdditional data for search indexing-
skip_enrich_urlbooleanWhether to skip URL enrichment for the activity-
textstringText content of the activity-
typestringType of activityRequired
user_idstringID of the user creating the activity-
visibilitystring (public, private, tag)Visibility setting for the activity-
visibility_tagstringIf visibility is 'tag', this is the tag name and is required-

Reading Activities with Enrichment

When you read activities from feeds, they are automatically enriched with additional data:

  • Comments: The latest 5 top-level comments (replies not included by default)
  • Reactions: Recent reactions and reaction counts
  • User data: Information about the activity author

To load more comments, replies, or additional data, use the dedicated loading methods described in the Comments and Reactions sections.

Overview of All Activity Fields

ActivityResponse

NameTypeDescriptionConstraints
attachmentsAttachment[]Media attachments for the activityRequired
bookmark_countintegerNumber of bookmarks on the activityRequired
collectionsobjectEnriched collection data referenced by this activityRequired
comment_countintegerNumber of comments on the activityRequired
commentsCommentResponse[]Comments on this activityRequired
created_atnumberWhen the activity was createdRequired
current_feedFeedResponseFeed context for this activity view. If an activity is added only to one feed, it's always set. If an activity is added to multiple feeds, it's only set when calling the GetOrCreateFeed endpoint.-
customobjectCustom data for the activityRequired
deleted_atnumberWhen the activity was deleted-
edited_atnumberWhen the activity was last edited-
expires_atnumberWhen the activity will expire-
feedsstring[]List of feed IDs containing this activityRequired
filter_tagsstring[]Tags for filteringRequired
hiddenbooleanIf this activity is hidden by this user (using activity feedback)Required
idstringUnique identifier for the activityRequired
interest_tagsstring[]Tags for user interestsRequired
is_watchedboolean--
latest_reactionsFeedsReactionResponse[]Recent reactions to the activityRequired
locationActivityLocationGeographic location related to the activity-
mentioned_usersUserResponse[]Users mentioned in the activityRequired
moderationModerationV2ResponseModeration information-
moderation_actionstring--
notification_contextNotificationContextNotification context data for the activity (if this is a reaction, comment, follow, etc.)-
own_bookmarksBookmarkResponse[]Current user's bookmarks for this activityRequired
own_reactionsFeedsReactionResponse[]Current user's reactions to this activityRequired
parentActivityResponseParent activity (if this is a reply/comment)-
pollPollResponseDataPoll attached to this activity-
popularityintegerPopularity score of the activityRequired
previewbooleanIf this activity is obfuscated for this user. For premium content where you want to show a previewRequired
reaction_countintegerNumber of reactions to the activityRequired
reaction_groupsobjectGrouped reactions by typeRequired
restrict_repliesstringControls who can reply to this activity. Values: everyone, people_i_follow, nobodyRequired
scorenumberRanking score for this activityRequired
search_dataobjectData for search indexingRequired
share_countintegerNumber of times the activity was sharedRequired
textstringText content of the activity-
typestringType of activityRequired
updated_atnumberWhen the activity was last updatedRequired
userUserResponseUser who created the activityRequired
visibilitystring (public, private, tag)Visibility setting for the activityRequired
visibility_tagstringIf visibility is 'tag', this is the tag name-

Adding Many Activities

You can also batch add activities. Here’s an example:

activities = [
    ActivityRequest(
        type="post",
        text="Batch activity 1",
        user_id=self.test_user_id,
        feeds=[self.test_feed.get_feed_identifier()],
    ),
    ActivityRequest(
        type="post",
        text="Batch activity 2",
        user_id=self.test_user_id,
        feeds=[self.test_feed.get_feed_identifier()],
    ),
]

response = self.client.feeds.upsert_activities(activities=activities)

Visibility Levels

When creating an activity, you can provide a visibility to the activity:

Please note that activity visibility is not the same as feed visibility

  • public: marks the activity as public - everyone who can view feed content, can see it
  • private: marks the activity as private - only feed owner can see it
  • tag:mytag: marks the activity as only visible to followers/members with the permission to see this tag

This visibility system is very flexible and allows you to build:

  • Apps like Patreon where only certain levels of users can see your content
  • Apps like Strava where it’s possible to share your activity with nobody, everyone or your followers
client.feeds.add_activity(
    feeds=["user:1"],
    type="post",
    text="Premium content",
    visibility="tag",
    visibility_tag="premium",
    user_id="<user id>"
)
# Premium users can see full activity, others a preview

For all the details on tag visibility read the Membership levels guide.

Updating & Deleting Activities

This example shows how to update or delete an activity:

response = self.client.feeds.update_activity(
    activity_id,
    text="Updated activity text from Python SDK",
    user_id=self.test_user_id,  # Required for server-side auth
    custom={"updated": True, "update_time": int(datetime.now().timestamp())},
)

Partial activity updates

A partial update can be used to set and unset specific fields when it is necessary to retain additional custom data fields on the object. AKA a patch style update. Both set and unset can be used in the same request. The dotted-notation is also available for both set and unset for the custom field.

// Partially set some fields
response = self.client.feeds.update_activity_partial(
    activity_id,
    user_id=self.test_user_id,  # Required for server-side auth
    set={
        "text": "Japan has over 6,800 islands.",
        "custom": {
            "topic": "fun facts",
            "color": "blue",
        }
    }
)

// Partially unset some fields
response = self.client.feeds.update_activity_partial(
    activity_id,
    user_id=self.test_user_id,  # Required for server-side auth
    unset=["custom.color"]
)

Get activity

Fetching a single activity.

When implementing an activity details page on the client-side it may be important to receive updates for the activity. The activity state is automatically updated on HTTP requests initiated from the client. To receive real-time updates (to receive updates from other users, for example someone else liked this post), you have to watch the feed the activity belongs to (or one of the feeds, in case it belongs to multiple feeds).

When fetching an activity that belongs to multiple feeds activity.current_feed will be empty. If you wish to display feed information alongside the activity, you have to fetch any of the containing feeds with a separate API call.

const activityWithStateUpdates =
  client.activityWithStateUpdates(activityId);
await activityWithStateUpdates.get({
  // Optionally fetch comments too
  comments: {
    limit: 10,
    depth: 2,
  },
});

// Subscribe to state updates
activityWithStateUpdates.state.subscribe((state) => {
  console.log(state.activity);
  console.log(state.comments_by_entity_id);
  // True if activity is being fetched
  console.log(state.is_loading);
});
// Comment pagination
activityWithStateUpdates.loadNextPageActivityComments;
activityWithStateUpdates.loadNextPageCommentReplies;

// Optionally start watching the feed
// If activity belongs to multiple feeds, it's up to you to choose which feed to watch
const fid = activityWithStateUpdates.currentState.activity!.feeds[0];
const [group, id] = fid.split(':');
const feed = client.feed(group, id);
let shouldWatch = false;
if (!feed.currentState.watch) {
  await feed.getOrCreate({
    watch: true,
    limit: 0,
    followers_pagination: { limit: 0 },
    following_pagination: { limit: 0 },
  });
}

// When leaving the page...
// dispose activity, this avoids refetching the activity if WebSocket reconnects
activityWithStateUpdates.dispose();
// you should stop watching the feed, unless your app has another component that watches the same feed
if (shouldWatch) {
  await feed.stopWatching();
}

// If you don't care about state updates, no need to call activityWithStateUpdates
await client.getActivity({
  id: activityId,
});

Adding activities to multiple feeds

The Stream API allows you to post an activity to multiple feeds, the maximum is 255.

When an activity is posted to multiple feeds, activity.current_feed field is only set when reading a feed (feed.getOrCreate):

  • When reading with current selector, it’s set to the feed we’re reading
  • For any other selector it’ll be one of the feeds the activity is posted to
    • If the user follows one of the feeds, it’s set to the followed feed (if more than one feed is followed, it’ll be one of the feeds)
  • For all other API calls (for example queryActivities, getActivity) and WebSocket events it’s not set
response = self.client.feeds.add_activity(
    type="post",
    feeds=[self.test_feed.get_feed_identifier()],
    text="This is a test activity from Python SDK",
    user_id=self.test_user_id,
    custom={
        "test_field": "test_value",
        "timestamp": int(datetime.now().timestamp()),
    },
)
© Getstream.io, Inc. All Rights Reserved.