# URL Previews

## Overview

When you add an **activity** or **comment** that contains a URL in the text, Stream can automatically fetch Open Graph (OG) metadata and attach a preview. This works for both activities and comments. You can disable enrichment per request with `skip_enrich_url`.

You can also use Stream's `getOG` endpoint to fetch OG data on your own.

## Sync vs async enrichment

`async_url_enrich_enabled` is an app-level setting that controls how URL enrichment is done:

- **When enabled (async):** The server saves the activity or comment first and enriches URLs in the background. The API responds without waiting for OG scraping.
- **When disabled (sync):** The server does URL enrichment before responding, with a ~4 second timeout for OG scraping.

Configure it via `updateApp` (server-side only):

<Tabs>

```js label="Node.js"
// Async enrichment: API returns immediately; OG data is attached in the background
await client.updateApp({ async_url_enrich_enabled: true });

// Sync enrichment: API waits for OG scraping (up to ~4s) before responding
await client.updateApp({ async_url_enrich_enabled: false });
```

```python label="Python"
# Async enrichment: API returns immediately; OG data is attached in the background
client.update_app(async_url_enrich_enabled=True)

# Sync enrichment: API waits for OG scraping (up to ~4s) before responding
client.update_app(async_url_enrich_enabled=False)
```

```ruby label="Ruby"
# Async enrichment: API returns immediately; OG data is attached in the background
client.common.update_app(GetStream::Generated::Models::UpdateAppRequest.new(async_url_enrich_enabled: true))

# Sync enrichment: API waits for OG scraping (up to ~4s) before responding
client.common.update_app(GetStream::Generated::Models::UpdateAppRequest.new(async_url_enrich_enabled: false))
```

```php label="PHP"
// Async enrichment: API returns immediately; OG data is attached in the background
$client->updateApp(new GeneratedModels\UpdateAppRequest(asyncUrlEnrichEnabled: true));

// Sync enrichment: API waits for OG scraping (up to ~4s) before responding
$client->updateApp(new GeneratedModels\UpdateAppRequest(asyncUrlEnrichEnabled: false));
```

```go label="Go"
// Async enrichment: API returns immediately; OG data is attached in the background
_, err = client.UpdateApp(ctx, &getstream.UpdateAppRequest{
  AsyncURLEnrichEnabled: getstream.PtrTo(true),
})
if err != nil {
  log.Fatal("Error updating app:", err)
}

// Sync enrichment: API waits for OG scraping (up to ~4s) before responding
_, err = client.UpdateApp(ctx, &getstream.UpdateAppRequest{
  AsyncURLEnrichEnabled: getstream.PtrTo(false),
})
```

```csharp label="C#"
// Async enrichment: API returns immediately; OG data is attached in the background
await client.UpdateAppAsync(new UpdateAppRequest { AsyncUrlEnrichEnabled = true });

// Sync enrichment: API waits for OG scraping (up to ~4s) before responding
await client.UpdateAppAsync(new UpdateAppRequest { AsyncUrlEnrichEnabled = false });
```

```java label="Java"
// Async enrichment: API returns immediately; OG data is attached in the background
UpdateAppRequest request = UpdateAppRequest.builder()
    .asyncUrlEnrichEnabled(true)
    .build();
common.updateApp(request).execute();

// Sync enrichment: API waits for OG scraping (up to ~4s) before responding
UpdateAppRequest syncRequest = UpdateAppRequest.builder()
    .asyncUrlEnrichEnabled(false)
    .build();
common.updateApp(syncRequest).execute();
```

</Tabs>

## Activities with URL enrichment

<Tabs>

```js label="React"
const response = await feed.addActivity({
  type: "post",
  text: "Check this out: https://example.com/article",
});
// response.activity.attachments may include OG preview when enrichment completes
```

```js label="React Native"
const response = await feed.addActivity({
  type: "post",
  text: "Check this out: https://example.com/article",
});
// response.activity.attachments may include OG preview when enrichment completes
```

```js label="JavaScript"
const response = await feed.addActivity({
  type: "post",
  text: "Check this out: https://example.com/article",
});
// response.activity.attachments may include OG preview when enrichment completes
```

```kotlin label="Kotlin"
val activity: Result<ActivityData> = feed.addActivity(
    request = FeedAddActivityRequest(
        text = "Check this out: https://example.com/article",
        type = "post"
    )
)
// activity.getOrNull()?.attachments may include OG preview when enrichment completes
```

```swift label="Swift"
let activity = try await feed.addActivity(
    request: .init(
        text: "Check this out: https://example.com/article",
        type: "post"
    )
)
// Enrichment is enabled by default; activity.attachments may include OG preview when ready
```

```dart label="Dart"
final activity = await feed.addActivity(
  request: FeedAddActivityRequest(
    text: 'Check this out: https://example.com/article',
    type: 'post',
  ),
);
// activity.attachments may include OG preview when enrichment completes
```

```js label="Node.js"
const response = await client.feeds.addActivity({
  feeds: ["user:eric"],
  type: "post",
  text: "Check this out: https://example.com/article",
  user_id: "eric",
});
// response.activity.attachments may include OG preview when enrichment completes
```

```python label="Python"
response = client.feeds.add_activity(
    feeds=["user:eric"],
    type="post",
    text="Check this out: https://example.com/article",
    user_id="eric",
)
# response["activity"]["attachments"] may include OG preview when enrichment completes
```

```ruby label="Ruby"
request = GetStream::Generated::Models::AddActivityRequest.new(
  feeds: ['user:eric'],
  type: 'post',
  text: 'Check this out: https://example.com/article',
  user_id: 'eric'
)

response = client.feeds.add_activity(request)
# response.activity.attachments may include OG preview when enrichment completes
```

```php label="PHP"
$response = $feedsClient->addActivity(
    new GeneratedModels\AddActivityRequest(
        feeds: ['user:eric'],
        type: 'post',
        text: 'Check this out: https://example.com/article',
        userID: 'eric'
    )
);
// $response->activity->attachments may include OG preview when enrichment completes
```

```go label="Go"
_, err = client.Feeds().AddActivity(context.Background(), &getstream.AddActivityRequest{
  Feeds:  []string{"user:eric"},
  Type:   "post",
  Text:   getstream.PtrTo("Check this out: https://example.com/article"),
  UserID: getstream.PtrTo("eric"),
})
if err != nil {
  log.Fatal("Error adding activity:", err)
}
```

```csharp label="C#"
var response = await _feedsV3Client.AddActivityAsync(
    new AddActivityRequest
    {
        Feeds = new[] { "user:eric" },
        Type = "post",
        Text = "Check this out: https://example.com/article",
        UserID = "eric"
    }
);
// response.Activity.Attachments may include OG preview when enrichment completes
```

```java label="Java"
AddActivityRequest request = AddActivityRequest.builder()
    .feeds(List.of("user:eric"))
    .type("post")
    .text("Check this out: https://example.com/article")
    .userID("eric")
    .build();

AddActivityResponse response = client.feeds().addActivity(request).execute().getData();
// response.getActivity().getAttachments() may include OG preview when enrichment completes
```

</Tabs>

## Comments with URL enrichment

Comments support the same behavior: URLs in the comment text are enriched by default. Use `skip_enrich_url: true` when adding a comment to skip enrichment.

<Tabs>

```js label="React"
const response = await client.addComment({
  comment: "See also: https://example.com/related",
  object_id: activityId,
  object_type: "activity",
});
```

```js label="React Native"
const response = await client.addComment({
  comment: "See also: https://example.com/related",
  object_id: activityId,
  object_type: "activity",
});
```

```js label="JavaScript"
const response = await client.addComment({
  comment: "See also: https://example.com/related",
  object_id: activityId,
  object_type: "activity",
});
```

```kotlin label="Kotlin"
val comment = feed.addComment(
    request = ActivityAddCommentRequest(
        comment = "See also: https://example.com/related",
        activityId = activityId
    )
)
```

```swift label="Swift"
let comment = try await feed.addComment(
    request: .init(
        comment: "See also: https://example.com/related",
        objectId: activityId,
        objectType: "activity"
        // skipEnrichUrl: true to disable
    )
)
```

```dart label="Dart"
final comment = await feed.addComment(
  request: ActivityAddCommentRequest(
    comment: 'See also: https://example.com/related',
    activityId: activityId,
    activityType: 'activity',
  ),
);
```

```js label="Node.js"
const response = await client.feeds.addComment({
  comment: "See also: https://example.com/related",
  object_id: activityId,
  object_type: "activity",
  user_id: "alice",
});
```

```python label="Python"
client.feeds.add_comment(
    comment="See also: https://example.com/related",
    object_id=activity_id,
    object_type="activity",
    user_id="alice",
)
```

```ruby label="Ruby"
client.feeds.add_comment(
  GetStream::Generated::Models::AddCommentRequest.new(
    comment: 'See also: https://example.com/related',
    object_id: activity_id,
    object_type: 'activity',
    user_id: 'alice'
  )
)
```

```php label="PHP"
$feedsClient->addComment(
    new GeneratedModels\AddCommentRequest(
        comment: 'See also: https://example.com/related',
        objectID: $activityId,
        objectType: 'activity',
        userID: 'alice'
    )
);
```

```go label="Go"
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
  Comment:    "See also: https://example.com/related",
  ObjectID:   activityID,
  ObjectType: "activity",
  UserID:     getstream.PtrTo("alice"),
})
if err != nil {
  log.Fatal("Error adding comment:", err)
}
```

```csharp label="C#"
await _feedsV3Client.AddCommentAsync(
    new AddCommentRequest
    {
        Comment = "See also: https://example.com/related",
        ObjectID = activityId,
        ObjectType = "activity",
        UserID = "alice"
    }
);
```

```java label="Java"
AddCommentRequest request = AddCommentRequest.builder()
    .comment("See also: https://example.com/related")
    .objectID(activityId)
    .objectType("activity")
    .userID("alice")
    .build();

client.feeds().addComment(request).execute().getData();
```

</Tabs>

## Skipping URL enrichment

To avoid fetching OG metadata for a given activity or comment, set `skip_enrich_url` to `true`.

<Tabs>

```js label="React"
// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: "https://example.com/page",
  skip_enrich_url: true,
});
// Comment
const commentResponse = await client.addComment({
  comment: "https://example.com/page",
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
});
```

```js label="React Native"
// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: "https://example.com/page",
  skip_enrich_url: true,
});
// Comment
const commentResponse = await client.addComment({
  comment: "https://example.com/page",
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
});
```

```js label="JavaScript"
// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: "https://example.com/page",
  skip_enrich_url: true,
});
// Comment
const commentResponse = await client.addComment({
  comment: "https://example.com/page",
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
});
```

```kotlin label="Kotlin"
// Activity
val activity = feed.addActivity(
    request = FeedAddActivityRequest(
        text = "https://example.com/page",
        type = "post",
        skipEnrichUrl = true
    )
)
// Comment
val comment = feed.addComment(
    request = ActivityAddCommentRequest(
        comment = "https://example.com/page",
        activityId = activityId,
        skipEnrichUrl = true
    )
)
```

```swift label="Swift"
// Activity
let activity = try await feed.addActivity(
    request: .init(
        text: "https://example.com/page",
        type: "post",
        skipEnrichUrl: true
    )
)
// Comment
let comment = try await feed.addComment(
    request: .init(
        comment: "https://example.com/page",
        objectId: activityId,
        objectType: "activity",
        skipEnrichUrl: true
    )
)
```

```dart label="Dart"
// Activity
final activity = await feed.addActivity(
  request: FeedAddActivityRequest(
    text: 'https://example.com/page',
    type: 'post',
    skipEnrichUrl: true,
  ),
);
// Comment
final comment = await feed.addComment(
  request: ActivityAddCommentRequest(
    comment: 'https://example.com/page',
    activityId: activityId,
    activityType: 'activity',
    skipEnrichUrl: true,
  ),
);
```

```js label="Node.js"
// Activity
const activityResponse = await client.feeds.addActivity({
  feeds: ["user:eric"],
  type: "post",
  text: "https://example.com/page",
  user_id: "eric",
  skip_enrich_url: true,
});
// Comment
const commentResponse = await client.feeds.addComment({
  comment: "https://example.com/page",
  object_id: activityId,
  object_type: "activity",
  user_id: "alice",
  skip_enrich_url: true,
});
```

```python label="Python"
# Activity
client.feeds.add_activity(
    feeds=["user:eric"],
    type="post",
    text="https://example.com/page",
    user_id="eric",
    skip_enrich_url=True,
)
# Comment
client.feeds.add_comment(
    comment="https://example.com/page",
    object_id=activity_id,
    object_type="activity",
    user_id="alice",
    skip_enrich_url=True,
)
```

```ruby label="Ruby"
# Activity
client.feeds.add_activity(
  GetStream::Generated::Models::AddActivityRequest.new(
    feeds: ['user:eric'],
    type: 'post',
    text: 'https://example.com/page',
    user_id: 'eric',
    skip_enrich_url: true
  )
)
# Comment
client.feeds.add_comment(
  GetStream::Generated::Models::AddCommentRequest.new(
    comment: 'https://example.com/page',
    object_id: activity_id,
    object_type: 'activity',
    user_id: 'alice',
    skip_enrich_url: true
  )
)
```

```php label="PHP"
// Activity
$feedsClient->addActivity(
    new GeneratedModels\AddActivityRequest(
        feeds: ['user:eric'],
        type: 'post',
        text: 'https://example.com/page',
        userID: 'eric',
        skipEnrichUrl: true
    )
);
// Comment
$feedsClient->addComment(
    new GeneratedModels\AddCommentRequest(
        comment: 'https://example.com/page',
        objectID: $activityId,
        objectType: 'activity',
        userID: 'alice',
        skipEnrichUrl: true
    )
);
```

```go label="Go"
// Activity
_, err = client.Feeds().AddActivity(context.Background(), &getstream.AddActivityRequest{
  Feeds:          []string{"user:eric"},
  Type:           "post",
  Text:           getstream.PtrTo("https://example.com/page"),
  UserID:         getstream.PtrTo("eric"),
  SkipEnrichURL:  getstream.PtrTo(true),
})
if err != nil {
  log.Fatal("Error adding activity:", err)
}
// Comment
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
  Comment:       "https://example.com/page",
  ObjectID:      activityID,
  ObjectType:    "activity",
  UserID:        getstream.PtrTo("alice"),
  SkipEnrichURL: getstream.PtrTo(true),
})
if err != nil {
  log.Fatal("Error adding comment:", err)
}
```

```csharp label="C#"
// Activity
await _feedsV3Client.AddActivityAsync(
    new AddActivityRequest
    {
        Feeds = new[] { "user:eric" },
        Type = "post",
        Text = "https://example.com/page",
        UserID = "eric",
        SkipEnrichURL = true
    }
);
// Comment
await _feedsV3Client.AddCommentAsync(
    new AddCommentRequest
    {
        Comment = "https://example.com/page",
        ObjectID = activityId,
        ObjectType = "activity",
        UserID = "alice",
        SkipEnrichURL = true
    }
);
```

```java label="Java"
// Activity
AddActivityRequest activityRequest = AddActivityRequest.builder()
    .feeds(List.of("user:eric"))
    .type("post")
    .text("https://example.com/page")
    .userID("eric")
    .skipEnrichURL(true)
    .build();
client.feeds().addActivity(activityRequest).execute().getData();
// Comment
AddCommentRequest commentRequest = AddCommentRequest.builder()
    .comment("https://example.com/page")
    .objectID(activityId)
    .objectType("activity")
    .userID("alice")
    .skipEnrichURL(true)
    .build();
client.feeds().addComment(commentRequest).execute().getData();
```

</Tabs>

## Reading URL preview attachments

Activities and comments return an `attachments` array. Enriched URL previews appear as attachments with fields such as:

- `og_scrape_url` – URL that was scraped
- `title` – OG title
- `title_link` – OG url
- `text` – OG description
- `image_url` or `thumb_url` – OG image
- `type` – e.g. `image`, `video`, or `audio`
- `asset_url` - link to the video/audio the link points to or empty

Use these fields to render link previews in your UI. Below: reading attachments from an activity and from a comment.

<Tabs>

```js label="React"
// From an activity
const { activity } = await feed.getActivity(activityId);
(activity.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});

// From a comment
const comment = activity.latest_replies?.[0];
(comment?.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});
```

```js label="React Native"
// From an activity
const { activity } = await feed.getActivity(activityId);
(activity.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});

// From a comment
const comment = activity.latest_replies?.[0];
(comment?.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});
```

```js label="JavaScript"
// From an activity
const { activity } = await feed.getActivity(activityId);
(activity.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});

// From a comment
const comment = activity.latest_replies?.[0];
(comment?.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});
```

```kotlin label="Kotlin"
// From an activity
val activity = client.activity(
    activityId = activityId,
    fid = FeedId(group = "user", id = "john")
)
activity.get()
activity.state.activity.value?.attachments?.forEach { attachment ->
    // use attachment.title, attachment.imageUrl, etc.
}

// From a comment
activity.state.comments.value.forEach { comment ->
    comment.attachments?.forEach { attachment ->
        // use attachment.title, attachment.imageUrl, etc.
    }
}
```

```swift label="Swift"
// From an activity
let activity = try await feed.getActivity(activityId: id, ...)
for attachment in activity.attachments ?? [] {
  // use attachment.title, attachment.imageURL, etc.
}

// From a comment
let comment = ... // e.g. from activity.latestReplies
for attachment in comment.attachments ?? [] {
  // use attachment.title, attachment.imageURL, etc.
}
```

```dart label="Dart"
// From an activity
final activity = await feed.getActivity(activityId, ...);
for (final a in activity.attachments ?? []) {
  // use a.title, a.imageURL, a.ogScrapeUrl, etc.
}

// From a comment
final comment = ...; // e.g. from activity.latestReplies
for (final a in comment.attachments ?? []) {
  // use a.title, a.imageURL, a.ogScrapeUrl, etc.
}
```

```js label="Node.js"
// From an activity
const { activity } = await client.feeds.getActivity(activityId);
(activity.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});

// From a comment
const comment = activity.latest_replies?.[0];
(comment?.attachments || []).forEach((a) => {
  // use a.title, a.image_url, a.og_scrape_url, etc.
});
```

```python label="Python"
# From an activity
r = client.feeds.get_activity(activity_id)
for a in r.get("activity", {}).get("attachments") or []:
    # use a.get("title"), a.get("image_url"), a.get("og_scrape_url"), etc.
    pass

# From a comment
for c in r.get("activity", {}).get("latest_replies") or []:
    for a in c.get("attachments") or []:
        # use a.get("title"), a.get("image_url"), a.get("og_scrape_url"), etc.
        pass
```

```ruby label="Ruby"
# From an activity
r = client.feeds.get_activity(activity_id)
(r.activity.attachments || []).each do |a|
  # use a.title, a.image_url, a.og_scrape_url, etc.
end

# From a comment
(r.activity.latest_replies || []).each do |comment|
  (comment.attachments || []).each do |a|
    # use a.title, a.image_url, a.og_scrape_url, etc.
  end
end
```

```php label="PHP"
// From an activity
$r = $feedsClient->getActivity($activityId);
foreach ($r->activity->attachments ?? [] as $a) {
  // use $a->title, $a->imageURL, $a->ogScrapeURL, etc.
}

// From a comment
foreach ($r->activity->latestReplies ?? [] as $comment) {
  foreach ($comment->attachments ?? [] as $a) {
    // use $a->title, $a->imageURL, $a->ogScrapeURL, etc.
  }
}
```

```go label="Go"
// From an activity
resp, _ := client.Feeds().GetActivity(ctx, activityID, nil)
for _, a := range resp.Data.Activity.Attachments {
  // use a.Title, a.ImageURL, a.OGScrapeURL, etc.
}

// From a comment
for _, c := range resp.Data.Activity.LatestReplies {
  for _, a := range c.Attachments {
    // use a.Title, a.ImageURL, a.OGScrapeURL, etc.
  }
}
```

```csharp label="C#"
// From an activity
var r = await _feedsV3Client.GetActivityAsync(activityId);
foreach (var a in r.Activity.Attachments ?? Array.Empty<Attachment>()) {
  // use a.Title, a.ImageURL, a.OGScrapeURL, etc.
}

// From a comment
foreach (var c in r.Activity.LatestReplies ?? Array.Empty<CommentResponse>()) {
  foreach (var a in c.Attachments ?? Array.Empty<Attachment>()) {
    // use a.Title, a.ImageURL, a.OGScrapeURL, etc.
  }
}
```

```java label="Java"
// From an activity
GetActivityResponse r = client.feeds().getActivity(activityId).execute().getData();
for (var a : r.getActivity().getAttachments()) {
  // use a.getTitle(), a.getImageURL(), a.getOGScrapeURL(), etc.
}

// From a comment
for (var c : r.getActivity().getLatestReplies()) {
  for (var a : c.getAttachments()) {
    // use a.getTitle(), a.getImageURL(), a.getOGScrapeURL(), etc.
  }
}
```

</Tabs>

## URL enrichment using `getOG` endpoint

You can fetch Open Graph metadata for a URL without adding an activity or comment by calling the Get OG endpoint. This is useful if you want to show URL enrichment to users before an activity or comment is sent. You can then persist OG data in `attachments` when sending the activity or comment (make sure to set `skip_enrich_url` to avoid duplicate previews).

If OG data can't be fetched for the given URL, the API request is rejected with an error.

<Tabs>

```js label="React"
const url = "https://example.com/article";
const attachments: Attachment[] = [];

try {
  const ogResponse = await client.getOG({ url });
  const { duration: _d, metadata: _m, ...ogData } = ogResponse;
  attachments.push(ogData);
} catch (error) {
  // OG may not be fetched for all URLs
  if (!(error instanceof StreamApiError && error.code === 4)) {
    throw error;
  }
}

// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: `Check this out: ${url}`,
  skip_enrich_url: true,
  attachment: attachments,
});
// Comment
const commentResponse = await client.addComment({
  comment: `See also: ${url}`,
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
  attachments: attachments,
});
```

```js label="React Native"
const url = "https://example.com/article";
const attachments: Attachment[] = [];

try {
  const ogResponse = await client.getOG({ url });
  const { duration: _d, metadata: _m, ...ogData } = ogResponse;
  attachments.push(ogData);
} catch (error) {
  // OG may not be fetched for all URLs
  if (!(error instanceof StreamApiError && error.code === 4)) {
    throw error;
  }
}

// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: `Check this out: ${url}`,
  skip_enrich_url: true,
  attachment: attachments,
});
// Comment
const commentResponse = await client.addComment({
  comment: `See also: ${url}`,
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
  attachments: attachments,
});
```

```js label="JavaScript"
const url = "https://example.com/article";
const attachments: Attachment[] = [];

try {
  const ogResponse = await client.getOG({ url });
  const { duration: _d, metadata: _m, ...ogData } = ogResponse;
  attachments.push(ogData);
} catch (error) {
  // OG may not be fetched for all URLs
  if (!(error instanceof StreamApiError && error.code === 4)) {
    throw error;
  }
}

// Activity
const activityResponse = await feed.addActivity({
  type: "post",
  text: `Check this out: ${url}`,
  skip_enrich_url: true,
  attachment: attachments,
});
// Comment
const commentResponse = await client.addComment({
  comment: `See also: ${url}`,
  object_id: activityId,
  object_type: "activity",
  skip_enrich_url: true,
  attachments: attachments,
});
```

```kotlin label="Kotlin"
val url = "https://example.com/article"
val og: Result<GetOGResponse> = client.getOG(url)
val ogAttachment = og.getOrThrow().toAttachment()

// Activity
val activity = feed.addActivity(
    request = FeedAddActivityRequest(
        text = "Check this out: $url",
        type = "post",
        skipEnrichUrl = true,
        attachments = listOf(ogAttachment)
    )
)
// Comment
val comment = feed.addComment(
    request = ActivityAddCommentRequest(
        comment = "See also: $url",
        activityId = activityId,
        skipEnrichUrl = true,
        attachments = listOf(ogAttachment)
    )
)
```

```swift label="Swift"
let url = "https://example.com/article"
let og = try await client.getOG(url: url)

// Activity
let activity = try await feed.addActivity(
    request: .init(
        text: "Check this out: \(url)",
        type: "post",
        skipEnrichUrl: true,
        attachments: [og.asAttachment]
    )
)
// Comment
let comment = try await feed.addComment(
    request: .init(
        comment: "See also: \(url)",
        objectId: activityId,
        objectType: "activity",
        skipEnrichUrl: true,
        attachments: [og.asAttachment]
    )
)
```

```dart label="Dart"
final url = 'https://example.com/article';
final og = await client.getOG(url: url);

// Activity
final activity = await feed.addActivity(
  request: FeedAddActivityRequest(
    text: 'Check this out: $url',
    type: 'post',
    skipEnrichUrl: true,
    attachments: [og],
  ),
);
// Comment
final comment = await feed.addComment(
  request: ActivityAddCommentRequest(
    comment: 'See also: $url',
    activityId: activityId,
    activityType: 'activity',
    skipEnrichUrl: true,
    attachments: [og],
  ),
);
```

```js label="Node.js"
const url = "https://example.com/article";
const attachments = [];

try {
  const ogResponse = await client.getOG({ url });
  const { duration: _d, metadata: _m, ...ogData } = ogResponse;
  attachments.push(ogData);
} catch (error) {
  // OG may not be fetched for all URLs
  if (!(error instanceof StreamApiError && error.code === 4)) {
    throw error;
  }
}

// Activity
const activityResponse = await client.feeds.addActivity({
  feeds: ["user:eric"],
  type: "post",
  text: `Check this out: ${url}`,
  user_id: "eric",
  skip_enrich_url: true,
  attachment: attachments,
});
// Comment
const commentResponse = await client.feeds.addComment({
  comment: `See also: ${url}`,
  object_id: activityId,
  object_type: "activity",
  user_id: "alice",
  skip_enrich_url: true,
  attachments: attachments,
});
```

```python label="Python"
url = "https://example.com/article"
og = client.get_og(url=url)

# Activity
client.feeds.add_activity(
    feeds=["user:eric"],
    type="post",
    text=f"Check this out: {url}",
    user_id="eric",
    skip_enrich_url=True,
    attachments=[og],
)
# Comment
client.feeds.add_comment(
    comment=f"See also: {url}",
    object_id=activity_id,
    object_type="activity",
    user_id="alice",
    skip_enrich_url=True,
    attachments=[og],
)
```

```ruby label="Ruby"
url = 'https://example.com/article'
og = client.get_og(GetStream::Generated::Models::GetOGRequest.new(url: url))

# Activity
client.feeds.add_activity(
  GetStream::Generated::Models::AddActivityRequest.new(
    feeds: ['user:eric'],
    type: 'post',
    text: "Check this out: #{url}",
    user_id: 'eric',
    skip_enrich_url: true,
    attachments: [og.to_h]
  )
)
# Comment
client.feeds.add_comment(
  GetStream::Generated::Models::AddCommentRequest.new(
    comment: "See also: #{url}",
    object_id: activity_id,
    object_type: 'activity',
    user_id: 'alice',
    skip_enrich_url: true,
    attachments: [og.to_h]
  )
)
```

```php label="PHP"
$url = 'https://example.com/article';
$og = $client->getOG(new GetOGRequest(url: $url));

// Activity
$feedsClient->addActivity(
    new GeneratedModels\AddActivityRequest(
        feeds: ['user:eric'],
        type: 'post',
        text: 'Check this out: ' . $url,
        userID: 'eric',
        skipEnrichUrl: true,
        attachments: [$og]
    )
);
// Comment
$feedsClient->addComment(
    new GeneratedModels\AddCommentRequest(
        comment: 'See also: ' . $url,
        objectID: $activityId,
        objectType: 'activity',
        userID: 'alice',
        skipEnrichUrl: true,
        attachments: [$og]
    )
);
```

```go label="Go"
url := "https://example.com/article"
og, err := client.OG().GetOG(context.Background(), &getstream.GetOGRequest{ URL: url })
if err != nil {
  log.Fatal(err)
}

// Activity
_, err = client.Feeds().AddActivity(context.Background(), &getstream.AddActivityRequest{
  Feeds:          []string{"user:eric"},
  Type:           "post",
  Text:           getstream.PtrTo("Check this out: " + url),
  UserID:         getstream.PtrTo("eric"),
  SkipEnrichURL:  getstream.PtrTo(true),
  Attachments:    []*getstream.Attachment{ogToAttachment(og)},
})
if err != nil {
  log.Fatal("Error adding activity:", err)
}
// Comment
_, err = client.Feeds().AddComment(context.Background(), &getstream.AddCommentRequest{
  Comment:       "See also: " + url,
  ObjectID:      activityID,
  ObjectType:    "activity",
  UserID:        getstream.PtrTo("alice"),
  SkipEnrichURL: getstream.PtrTo(true),
  Attachments:   []*getstream.Attachment{ogToAttachment(og)},
})
if err != nil {
  log.Fatal("Error adding comment:", err)
}
```

```csharp label="C#"
var url = "https://example.com/article";
var og = await _client.GetOGAsync(new GetOGRequest { URL = url });

// Activity
await _feedsV3Client.AddActivityAsync(
    new AddActivityRequest
    {
        Feeds = new[] { "user:eric" },
        Type = "post",
        Text = "Check this out: " + url,
        UserID = "eric",
        SkipEnrichURL = true,
        Attachments = new[] { OgToAttachment(og) }
    }
);
// Comment
await _feedsV3Client.AddCommentAsync(
    new AddCommentRequest
    {
        Comment = "See also: " + url,
        ObjectID = activityId,
        ObjectType = "activity",
        UserID = "alice",
        SkipEnrichURL = true,
        Attachments = new[] { OgToAttachment(og) }
    }
);
```

```java label="Java"
String url = "https://example.com/article";
GetOGResponse og = client.og().getOG(url).execute().getData();

// Activity
AddActivityRequest activityRequest = AddActivityRequest.builder()
    .feeds(List.of("user:eric"))
    .type("post")
    .text("Check this out: " + url)
    .userID("eric")
    .skipEnrichURL(true)
    .attachments(List.of(ogToAttachment(og)))
    .build();
client.feeds().addActivity(activityRequest).execute().getData();
// Comment
AddCommentRequest commentRequest = AddCommentRequest.builder()
    .comment("See also: " + url)
    .objectID(activityId)
    .objectType("activity")
    .userID("alice")
    .skipEnrichURL(true)
    .attachments(List.of(ogToAttachment(og)))
    .build();
client.feeds().addComment(commentRequest).execute().getData();
```

</Tabs>


---

This page was last updated at 2026-07-17T15:25:05.991Z.

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