Export Data

You can export your app's feeds data as files: to keep a backup, move data to another Stream app, or load it into your own systems. An export runs in the background and writes JSON Lines files in the same format the importer reads.

Exports are server-side only.

Export the whole app

exportFeedsApp starts an export of every feeds data type in the app and returns a task_id:

response, err := client.Feeds().ExportFeedsApp(ctx, &getstream.ExportFeedsAppRequest{})
if err != nil {
    log.Fatal(err)
}
fmt.Println(response.Data.TaskID)

Both fields are optional:

Field Type Description
types array Export only these types, for example ["activity", "comment"]. Empty or absent exports every type. poll and poll_vote need activity too, because polls are found through the activities that use them.
include_deleted boolean Defaults to true. Set it to false to leave out soft-deleted users, feed groups, feeds, collections, activities and comments.

The type names are those of the import file format: user, feed_group, feed_view, user_interest, collection, feed, poll, poll_vote, activity, follow, comment, reaction and bookmark.

Export one type

exportFeedsByType exports one of activities, comments, reactions or follows, optionally filtered. Without filters it exports the whole type:

response, err := client.Feeds().ExportFeedsByType(ctx, "comments", &getstream.ExportFeedsByTypeRequest{
    UserID: getstream.PtrTo("jane"),
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(response.Data.TaskID)
Field Type Description
user_id string Only rows of this user. For follows: the user's own follows, and other users' follows of the user's feeds.
created_at_from timestamp Only rows created at or after this time.
created_at_to timestamp Only rows created before this time. Must be later than created_at_from.
activity_ids array comments and reactions only. Only the comments or reactions of these activities, replies and comment reactions included. At most 100 IDs, each listed once.
include_deleted boolean Defaults to false. Set it to true to include soft-deleted activities and comments.

Only the filter combinations below are accepted. Any other combination is rejected with a 400 error that names the filter that isn't allowed:

Type Accepted filters
activities user_id, a created_at range, or both
comments user_id, a created_at range, activity_ids, user_id with a range, activity_ids with either
reactions user_id, activity_ids, user_id with a range, activity_ids with either
follows user_id, or user_id with a range

include_deleted: true can't be combined with user_id on activities or comments, or with a created_at range on comments unless activity_ids is set too. Reactions and follows have no soft-deleted rows.

Other types are available only through the whole-app export, with types. A per-type export holds only that type. It doesn't carry the users, feeds or activities its rows point to, so use the whole-app export when you need a self-contained copy.

The whole-app export has no time filter. A created_at range is available only on the per-type export, with the combinations above.

Get the files

Poll the task until its status is completed. The result holds a link to manifest.json and one link for every data file:

task, err := client.GetTask(ctx, response.Data.TaskID, &getstream.GetTaskRequest{})
if err != nil {
    log.Fatal(err)
}
if task.Data.Status == "completed" {
    fmt.Println(task.Data.Result["url"]) // manifest.json
    fmt.Println(task.Data.Result["files"])
}
{
  "task_id": "9f65c213-673c-4689-adda-09b0efddf2d0",
  "status": "completed",
  "result": {
    "url": "https://.../manifest.json?...",
    "files": [
      {
        "path": ".../users/part-0001.jsonl",
        "url": "https://.../users/part-0001.jsonl?..."
      },
      {
        "path": ".../09-activities/part-0001.jsonl",
        "url": "https://.../09-activities/part-0001.jsonl?..."
      }
    ]
  }
}

The links are signed URLs that expire after 24 hours. Each poll of the task signs them again, so poll the task again when you need fresh links.

While the export is queued or running, the task is pending. A failed export reports its error.

Only one export runs per app at a time. Starting another while one is queued or running returns an error until the first one finishes, fails or is cancelled.

Webhooks

When an export finishes, your app's webhook receives one of these events:

Event Sent when Payload
feeds.export.success The export completed task_id, manifest_path, started_at, finished_at
feeds.export.error The export failed task_id, error, started_at, finished_at

The event is a notification: get the download links from the task.

Cancel an export

cancelExportFeeds stops a queued or running export and deletes the files it already wrote. A cancelled export sends no webhook, and its task reports canceled.

_, err = client.Feeds().CancelExportFeeds(ctx, response.Data.TaskID, &getstream.CancelExportFeedsRequest{})

Files

An export is a folder with a manifest and one sub-folder per type:

manifest.json
users/part-0001.jsonl
02-feed_groups/part-0001.jsonl
03-feed_views/part-0001.jsonl
04-user_interests/part-0001.jsonl
05-collections/part-0001.jsonl
06-feeds/part-0001.jsonl
07-polls/part-0001.jsonl
08-poll_votes/part-0001.jsonl
09-activities/part-0001.jsonl
09-activities/part-0002.jsonl
10-follows/part-0001.jsonl
11-comments/part-0001.jsonl
12-reactions/part-0001.jsonl
13-bookmarks/part-0001.jsonl
  • Each part file is under 300 MB. A type with no rows has no folder.
  • The numbers in the folder names set the import order: a record only points to records in its own file or an earlier one. Keep the files and their order as they are.
  • Each line is one record, {"type": "<type>", "data": {...}}, with your custom fields under custom:
{
  "type": "activity",
  "data": {
    "created_at": "2026-10-20T11:44:14.761632Z",
    "custom": { "mood": "happy" },
    "fids": ["user:u1"],
    "id": "a1",
    "text": "Which day?",
    "type": "post",
    "updated_at": "2026-10-20T11:44:14.761632Z",
    "user_id": "u1",
    "visibility": "public"
  }
}
  • Soft-deleted rows carry deleted_at.
  • Counts and other fields Stream computes, such as reaction_groups, latest_reactions and comment depth, are not exported: they are rebuilt on import. Feed members and bookmark folders are not exported yet.
  • Only polls used by an exported activity are exported, with their votes.

An export is a snapshot of rows by creation time: rows created after the export started are left out. Changes to existing rows during the export can show up in it.

The manifest

manifest.json describes the export:

{
  "format_version": 1,
  "app_id": 1234,
  "exported_at": "2026-10-15T12:00:00Z",
  "custom_format": "nested",
  "types": [
    {
      "type": "activity",
      "folder": "09-activities",
      "record_count": 1500000,
      "files": [
        {
          "path": "09-activities/part-0001.jsonl",
          "record_count": 750000,
          "bytes": 298123456
        }
      ]
    }
  ],
  "excluded_types": ["feed_member", "bookmark_folder"],
  "excluded_fields": { "activity": ["comment_count", "..."] },
  "caps_hit": [{ "type": "activity", "cap": "type", "limit": 1000000 }],
  "truncated": true,
  "filter": {
    "user_id": "jane",
    "types": ["activity"],
    "include_deleted": false
  }
}
Field Description
types Every exported type, with its folder, record count and files.
excluded_types, excluded_fields What the export leaves out by design.
caps_hit, truncated Present only when a record limit stopped the export early. See Record limits.
filter Present only on a partial export (per-type, filtered, or include_deleted: false): the filters it used.

Record limits

One export reads at most 1,000,000 records per type and 5,000,000 records in total. The limits are set when the export starts.

When a type reaches its limit, the export stops that type and continues with the next one. A comment thread is never split, so the comments folder can go slightly over the limit. When the export reaches the total limit, it stops the current type and skips the rest. The export still completes, and the manifest says what was left out:

  • truncated is true;
  • caps_hit lists each affected type, with cap set to "type" or "job" (the total limit) and the limit that was reached.

To export more, split the export with the per-type filters, for example by created_at range. If you need higher limits, contact support.

Importing an export

An export imports with the importer. Upload the folder as it is, keeping the paths: the importer skips manifest.json, reads users/ first and then the other folders in order.

Two import settings matter:

  • custom_format: "nested": export records keep custom fields under custom.
  • on_missing_reference: "warn": an export copies your data as it is, including references that already point to deleted rows, and a capped or filtered export leaves rows out. With warn, a record whose reference is missing still imports and is counted in the import result. With the default, one missing reference fails its whole file.

When you import an export of an app that was migrated from Feeds v2 into another app of your organization, the target app gets the same size limits as the source.