Session Replay API
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_idparameter 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_tokenfrom the previous response as-is. - The
sort_orderparameter must be consistent across all pages of a paginated request. Passing apage_tokenfrom anascrequest withsort_order=descreturns a 400 error. amplitude_idandreplay_idare mutually exclusive. Passing both returns a 400 error.replay_idandpage_tokenare mutually exclusive. Passing both returns a 400 error.- Each
replay_idvalue must usedevice_id/session_idformat. A leading or trailing slash returns a 400 error. - You can pass up to 100
replay_idvalues per request. Passing 101 or more returns a 400 error. amplitude_idmust be a valid integer. A non-numeric value returns a 400 error.- When you use
replay_id, the API ignorespage_sizeand always returnsnext_page_tokenasnull.
EU data residency
For EU data residency, use https://eu.amplitude.com as the base URL instead of https://amplitude.com. For example:
- List session replays:
GET https://eu.amplitude.com/api/1/session-replays - Get session replay files:
GET https://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 --location 'https://amplitude.com/api/1/session-replays' \
-u '{api_key}:{secret_key}'
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
{
"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 --location 'https://amplitude.com/api/1/session-replays/files?replay_id={device_id}%2F{session_id}' \
-u '{api_key}:{secret_key}'
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
{
"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 events ready to pass to an rrweb player.
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
}
The result is a JSON array of rrweb events:
[
{ "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:
- gzip decompress the file, then JSON parse → array of packed strings.
- zlib decompress each string: each element is a JSON-encoded, zlib-compressed (DEFLATE) binary payload → rrweb event object.
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
});
}
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. Use Amplitude's fork rather than upstream rrweb, because the fork includes fixes that may be incompatible with the upstream version.
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. |
Was this helpful?