# Session Replay API

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

## Authentication and request requirements

- All endpoints use HTTP Basic auth. Use your project's API key as the username and secret key as the password.
- The `project_id` parameter isn't needed. The authenticated API key identifies the project.
- Presigned file URLs expire after 15 minutes.
- Pagination cursors are opaque strings. Don't construct or modify them. Pass `next_page_token` from the previous response as-is.
- The `sort_order` parameter must be consistent across all pages of a paginated request. Passing a `page_token` from an `asc` request with `sort_order=desc` returns a 400 error.
- `amplitude_id` and `replay_id` are mutually exclusive. Passing both returns a 400 error.
- `replay_id` and `page_token` are mutually exclusive. Passing both returns a 400 error.
- Each `replay_id` value must use `device_id/session_id` format. A leading or trailing slash returns a 400 error.
- You can pass up to 100 `replay_id` values per request. Passing 101 or more returns a 400 error.
- `amplitude_id` must be a valid integer. A non-numeric value returns a 400 error.
- When you use `replay_id`, the API ignores `page_size` and always returns `next_page_token` as `null`.

## EU data residency

For EU data residency, use `https://analytics.eu.amplitude.com` as the base URL instead of `https://amplitude.com`. For example:

- List session replays: `GET https://analytics.eu.amplitude.com/api/1/session-replays`
- Get session replay files: `GET https://analytics.eu.amplitude.com/api/1/session-replays/files`

## List session replays

Returns a paginated list of session replays for the authenticated project.

`GET https://amplitude.com/api/1/session-replays`

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

#### Example: List replays with filters and descending sort

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays?start_time=2024-01-01T00%3A00%3A00Z&end_time=2024-01-31T23%3A59%3A59Z&page_size=25&sort_order=desc' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays?start_time=2024-01-01T00%3A00%3A00Z&end_time=2024-01-31T23%3A59%3A59Z&page_size=25&sort_order=desc HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

#### Example: Filter replays by Amplitude user ID

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays?amplitude_id=123456' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays?amplitude_id=123456 HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

#### Example: Fetch specific replays by replay ID

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays?replay_id=abc123%2F1700000000000&replay_id=def456%2F1700000001000' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays?replay_id=abc123%2F1700000000000&replay_id=def456%2F1700000001000 HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

### Query parameters

| Name | Description |
| --- | --- |
| `start_time` | Optional. ISO 8601 string. Lower bound on replay `start_time`, inclusive. For example, `2024-01-01T00:00:00Z`. |
| `end_time` | Optional. ISO 8601 string. Upper bound on replay `start_time`, inclusive. For example, `2024-01-31T23:59:59Z`. |
| `amplitude_id` | Optional. Integer. Filters results to replays belonging to a specific Amplitude user ID. Mutually exclusive with `replay_id`. |
| `replay_id` | Optional. String. Filters results to specific replays by ID, in `device_id/session_id` format. URL-encode the `/` separator as `%2F`. Repeat this parameter to request up to 100 replays. Mutually exclusive with `amplitude_id` and `page_token`. When you use this parameter, `page_size` is ignored and `next_page_token` is always `null`. |
| `page_size` | Optional. Integer. Number of results per page. Default is `50`, maximum is `200`. Ignored when `replay_id` is specified. |
| `page_token` | Optional. String. Opaque pagination cursor from a previous response's `next_page_token`. Mutually exclusive with `replay_id`. |
| `sort_order` | Optional. String. `asc` returns oldest replays first (default). `desc` returns newest replays first. Must be consistent across pages when using `page_token`. |

### Response

```json
{
  "session_replays": [
    {
      "replay_id": "string",
      "session_id": "string",
      "device_id": "string",
      "amplitude_id": 123456,
      "start_time": "2024-01-01T00:00:00Z",
      "end_time": "2024-01-01T00:05:00Z",
      "retention_in_days": 90
    }
  ],
  "next_page_token": "string | null"
}
```

| Property | Description |
| --- | --- |
| `session_replays` | Array of session replay objects. |
| `session_replays[].replay_id` | Unique identifier for the replay, in `device_id/session_id` format. Use this as the `replay_id` parameter when fetching files. |
| `session_replays[].session_id` | The session identifier. |
| `session_replays[].device_id` | The device identifier. |
| `session_replays[].amplitude_id` | The Amplitude user ID. |
| `session_replays[].start_time` | ISO 8601 timestamp of when the session started. |
| `session_replays[].end_time` | ISO 8601 timestamp of when the session ended. |
| `session_replays[].retention_in_days` | Number of days Amplitude retains the replay data. |
| `next_page_token` | Opaque cursor to pass as `page_token` to retrieve the next page. `null` when there are no more results. |

---

## Get session replay files

Returns a paginated list of presigned S3 URLs for the event files belonging to a specific replay. Each URL points to a gzip-compressed JSON array of rrweb events. Amplitude orders files by key, which encodes start time.

`GET https://amplitude.com/api/1/session-replays/files`

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays/files?replay_id={device_id}%2F{session_id}' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays/files?replay_id={device_id}%2F{session_id} HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

#### Example: Fetch v2 files with pagination

#### cURL

```curl
curl --location 'https://amplitude.com/api/1/session-replays/files?replay_id={device_id}%2F{session_id}&version=2&page_size=50&page_token={page_token}' \
-u '{api_key}:{secret_key}'
```

#### HTTP

```bash
GET /api/1/session-replays/files?replay_id={device_id}%2F{session_id}&version=2&page_size=50&page_token={page_token} HTTP/1.1
Host: amplitude.com
Authorization: Basic {api-key}:{secret-key} #credentials must be base64 encoded
```

### Query parameters

| Name | Description |
| --- | --- |
| `replay_id` | Required. String. The replay identifier, in `device_id/session_id` format. URL-encode the `/` separator as `%2F`. |
| `version` | Optional. Integer. Recording format version. `2` or `3`. Default is `3`. |
| `page_size` | Optional. Integer. Number of files per page. Default is `100`, maximum is `1000`. |
| `page_token` | Optional. String. Opaque pagination cursor from a previous response's `next_page_token`. |

### Response

```json
{
  "files": [
    "https://s3.amazonaws.com/...presigned-url-1...",
    "https://s3.amazonaws.com/...presigned-url-2..."
  ],
  "next_page_token": "string | null"
}
```

| Property | Description |
| --- | --- |
| `files` | Array of presigned S3 URLs. Each URL serves a gzip-compressed JSON array of rrweb events. URLs expire after 15 minutes. |
| `next_page_token` | Opaque cursor to pass as `page_token` to retrieve the next page. `null` when there are no more files. |

## Decompress and parse replay files

The format of each file depends on the `version` you requested.

### Version 3

Each file uses gzip compression. Decompress the file to get a JSON array of [rrweb](https://github.com/amplitude/rrweb) events ready to pass to an rrweb player.

#### JavaScript

```javascript
async function fetchReplayEvents(fileUrl) {
  const response = await fetch(fileUrl);
  // The response is gzip-compressed; fetch decompresses automatically in browsers.
  // In Node.js 18+, use the DecompressionStream API or the zlib module.
  const buffer = await response.arrayBuffer();
  const text = new TextDecoder().decode(buffer);
  return JSON.parse(text); // array of rrweb events
}
```

#### Python

```python
import gzip
import json
import urllib.request

def fetch_replay_events(file_url):
    with urllib.request.urlopen(file_url) as response:
        decompressed = gzip.decompress(response.read())
    return json.loads(decompressed)  # list of rrweb events
```

#### cURL

```bash
curl -s '{presigned_url}' | gunzip | python3 -m json.tool
```

The result is a JSON array of rrweb events:

```json
[
  { "type": 4, "data": { "href": "https://example.com", "width": 1440, "height": 900 }, "timestamp": 1700000000000 },
  { "type": 2, "data": { ... }, "timestamp": 1700000000050 },
  ...
]
```

### Version 2

Version 2 files require two decompression steps:

1. **gzip decompress** the file, then JSON parse → array of packed strings.
2. **zlib decompress** each string: each element is a JSON-encoded, zlib-compressed (DEFLATE) binary payload → rrweb event object.

#### JavaScript

```javascript
const zlib = require("zlib");

async function fetchReplayEventsV2(fileUrl) {
  const response = await fetch(fileUrl);
  const buffer = Buffer.from(await response.arrayBuffer());

  // Step 1: gzip decompress the file, then JSON parse → array of packed strings
  const packedStrings = JSON.parse(zlib.gunzipSync(buffer).toString("utf8"));

  // Step 2: unpack each string
  return packedStrings.map((packed) => {
    // Each packed string is itself a JSON string whose value is a latin1-encoded
    // binary blob of zlib-compressed event data.
    const compressedBinary = JSON.parse(packed);
    const buf = Buffer.from(compressedBinary, "latin1");
    return JSON.parse(zlib.inflateSync(buf).toString("utf8")); // rrweb event
  });
}
```

#### Python

```python
import gzip
import json
import zlib
import urllib.request

def fetch_replay_events_v2(file_url):
    with urllib.request.urlopen(file_url) as response:
        # Step 1: gzip decompress, then JSON parse → list of packed strings
        packed_strings = json.loads(gzip.decompress(response.read()))

    # Step 2: unpack each string
    events = []
    for packed in packed_strings:
        # Each packed string is a JSON string whose value is a latin1-encoded
        # binary blob of zlib-compressed event data.
        compressed_binary = json.loads(packed)
        buf = compressed_binary.encode('latin1')
        events.append(json.loads(zlib.decompress(buf)))
    return events
```

### Play back events

To replay a full session, fetch all files for a replay in order, unpack each file, concatenate the event arrays, and pass the result to Amplitude's [rrweb player](https://github.com/amplitude/rrweb). Use Amplitude's fork rather than upstream rrweb, because the fork includes fixes that may be incompatible with the upstream version.

```javascript
const events = (await Promise.all(fileUrls.map(fetchReplayEvents))).flat();
rrweb.replay({ events, root: document.getElementById("player") });
```

## Status and error codes

| Code | Description |
| --- | --- |
| `200 OK` | Successful request. |
| `400 Bad Request` | Invalid parameter value. Check the error message for details. |
| `401 Unauthorized` | Missing or invalid API credentials. |
| `404 Not Found` | No data found for the given `replay_id`. Amplitude only returns this error on the first page. |
