> ## Documentation Index
> Fetch the complete documentation index at: https://docs.browserbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Recording downloads

> Assemble and download session recordings as MP4 files, one per page.

<Info>
  The Recording Downloads API turns a session's recording into downloadable MP4 files, one per recorded page (tab). Assembly runs asynchronously: request the downloads, then poll for status and a short-lived download URL. To stream playback in-app instead of downloading files, use the [Session Replay API](/platform/browser/observability/session-replay).
</Info>

Download a completed session's recording as MP4 files, one per tab. Browserbase assembles them on demand after the session ends.

## Quickstart

Given a `sessionId` from a session that has ended, request the downloads, then poll until each page is `COMPLETED`.

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    import { Browserbase } from "@browserbasehq/sdk";

    const sessionId = "<your session ID>";
    const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });

    // Request one MP4 per recorded page (tab); each page starts PENDING.
    await bb.sessions.recording.downloads.create(sessionId);

    // Poll until every page has finished assembling.
    let downloads;
    do {
      await new Promise((resolve) => setTimeout(resolve, 3000));
      ({ downloads } = await bb.sessions.recording.downloads.list(sessionId));
    } while (downloads.some((page) => page.status === "PENDING"));

    for (const page of downloads) {
      console.log(page.pageId, page.status, page.downloadUrl);
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import time

    from browserbase import Browserbase

    session_id = "<your session ID>"
    bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])

    # Request one MP4 per recorded page (tab); each page starts PENDING.
    bb.sessions.recording.downloads.create(session_id)

    # Poll until every page has finished assembling.
    while True:
        downloads = bb.sessions.recording.downloads.list(session_id).downloads
        if not any(page.status == "PENDING" for page in downloads):
            break
        time.sleep(3)

    for page in downloads:
        print(page.page_id, page.status, page.download_url)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # Request the downloads (each page starts PENDING).
    curl -X POST https://api.browserbase.com/v1/sessions/$SESSION_ID/recording/downloads \
      -H "x-bb-api-key: $BROWSERBASE_API_KEY"

    # Poll for status and download URLs.
    curl https://api.browserbase.com/v1/sessions/$SESSION_ID/recording/downloads \
      -H "x-bb-api-key: $BROWSERBASE_API_KEY"
    ```
  </Tab>
</Tabs>

## How it works

Assembly of the recording is asynchronous, driven by two calls on the same path:

<Steps>
  <Step title="Request the downloads (POST)">
    `POST /v1/sessions/{id}/recording/downloads` enqueues one MP4 per recorded page (tab). Assuming the session has ended and is still within its retention window, it returns `202` and marks every page `PENDING`. Re-posting re-enqueues all pages and retries any that failed, so it is safe to call again.
  </Step>

  <Step title="Poll for status (GET)">
    `GET /v1/sessions/{id}/recording/downloads` returns the current state of each page. Poll it until the pages you need reach `COMPLETED` (or `FAILED`).
  </Step>

  <Step title="Download the MP4">
    Each `COMPLETED` page carries a short-lived `downloadUrl`. Opening it downloads the MP4 directly and saves it as `{sessionId}-{pageId}.mp4`.
  </Step>
</Steps>

### Status values

Each page reports one of four states:

| Status          | Meaning                                                          |
| --------------- | ---------------------------------------------------------------- |
| `NOT_REQUESTED` | You haven't requested a download for the session yet.            |
| `PENDING`       | Browserbase is assembling the MP4 (queued or in progress).       |
| `COMPLETED`     | The MP4 is ready; `downloadUrl` is present on non-BYOS projects. |
| `FAILED`        | Assembly failed. POST again to retry the page.                   |

## Response shape

Each entry corresponds to one recorded page (tab).

```json theme={null}
{
  "downloads": [
    {
      "pageId": "0",
      "status": "COMPLETED",
      "downloadUrl": "https://.../recording-renditions/.../0.mp4?token=...",
      "completedAt": "2026-06-30T18:24:05.000Z"
    },
    {
      "pageId": "1",
      "status": "PENDING"
    }
  ]
}
```

Field semantics:

* `pageId` identifies the recorded page within the session, ascending from `"0"`.
* `downloadUrl` and `completedAt` are present only when `status` is `COMPLETED` on a standard (non-BYOS) project.

## Multitab

Browserbase records up to 10 tabs open concurrently per session, and produces one MP4 per recorded page. Request downloads once; poll the list to collect each page's file as it finishes.

## Download URL expiration

Browserbase signs each `downloadUrl`, and it expires **six hours** after the API issues it. The GET endpoint re-mints the URL on every call, so if a link expires, list the downloads again to get a fresh one.

## Availability window

You can assemble recording downloads while Browserbase still retains the session's recording source, for up to **31 days** after the session ends on non-BYOS projects. After that, the source is gone and the API returns `410 Gone`.

<Note>
  Downloads become available only after a session **ends**. Requesting them for a running session returns `409 Conflict`.
</Note>

## Bring your own storage (BYOS)

On projects using [Bring Your Own Storage](/account/enterprise/byos-setup-guide), Browserbase writes each assembled MP4 to your own S3 bucket instead of Browserbase storage. The API still reports per-page `status` but omits `downloadUrl` and `completedAt`, so fetch the files from your bucket. BYOS projects retain the recording source for **24 hours**.

## Rate limits

The POST endpoint is limited to **5 requests per minute** per project, since each call kicks off assembly. [BYOS](/account/enterprise/byos-setup-guide) enables higher rate limits for enterprise customers. See [Concurrency & Rate Limits](/optimizations/concurrency/overview) for the retry pattern.

## Recording downloads vs. Session replay

* **Recording downloads** produce standalone **MP4 files** you fetch and store (best for archiving and offline review).
* **[Session replay](/platform/browser/observability/session-replay)** streams the recording as **HLS** for embedded, in-app playback.

Both draw from the same underlying session recording.

## Disabling recordings

Sessions created with `recordSession: false` produce no recording, so there is nothing to download; both endpoints return `404 Not Found`. See [Session Recording](/platform/browser/observability/session-recording#disabling-video-recordings).

## Troubleshooting

**GET returns `409 Conflict`:**

* The session has not ended yet. Browserbase assembles downloads only after a session completes.

**GET returns `410 Gone`:**

* The recording aged out of its retention window (up to 31 days on non-BYOS projects, 24 hours when using BYOS). Browserbase can no longer assemble it.

**A page stays `PENDING`:**

* Large recordings take longer to assemble. Keep polling; re-POST only if a page reports `FAILED`.

**`downloadUrl` is missing on a `COMPLETED` page:**

* The project uses BYOS. The MP4 is in your own bucket; the API does not return a signed URL for BYOS renditions.

## Related

<CardGroup cols={2}>
  <Card title="Session replay" icon="play" href="/platform/browser/observability/session-replay">
    Stream a session's recording as HLS for embedded, in-app playback.
  </Card>

  <Card title="Session recording" icon="video" href="/platform/browser/observability/session-recording">
    Replay every session as a video recording to debug after the fact.
  </Card>

  <Card title="Bring your own storage" icon="bucket" href="/account/enterprise/byos-setup-guide">
    Route recording renditions to your own S3 bucket.
  </Card>

  <Card title="Recording downloads API" icon="code" href="/reference/api/create-session-recording-downloads">
    API reference for requesting and listing per-page MP4 downloads.
  </Card>
</CardGroup>
