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):

// 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),
})

Activities with URL enrichment

_, 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)
}

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.

_, 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)
}

Skipping URL enrichment

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

// 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)
}

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.

// 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.
  }
}

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.

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)
}