---
title: List Screen Records
slug: devicepark/sdk-documentation/sessions/list-screen-records
description: List and filter screen recordings generated for a Device Park session.
docTags: 
createdAt: 2026-08-10T08:03:45.197Z
---

Use this method to retrieve the stored recording-segment metadata for one Device Park session. Recording must be enabled when the session is started.

`screenRecords(...)` and `Session.videoRecordUrl` serve different purposes:

| SDK value                              | Returns                                    | Use it for                                              |
| -------------------------------------- | ------------------------------------------ | ------------------------------------------------------- |
| `client.sessions().screenRecords(...)` | `PageDto<ScreenRecord>` JSON               | Listing and filtering the stored recording segments     |
| `Session.videoRecordUrl`               | RFC 8216-compatible HLS media-playlist URL | Playing the complete session recording in an HLS player |

Do not pass a `ScreenRecord.downloadUrl` to an HLS player. It addresses one stored recording segment; `videoRecordUrl` addresses the playlist that orders the segments into the complete recording.

## Before You Begin

- Start the session with `videoRecording(true)`.
- Store the returned `sessionId`.
- Stop the session and allow recording processing to complete.

## SDK Method

| Java                                                  | Node.js                                                      | .NET                                                            | Returns                 |
| ----------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------- | ----------------------- |
| `client.sessions().screenRecords(sessionId, request)` | `await client.sessions().screenRecords(sessionId, request?)` | `await client.Sessions.ScreenRecordsAsync(sessionId, request?)` | `PageDto<ScreenRecord>` |

## Request Fields

| Input               | Type            | Required | Default | Description                    |
| ------------------- | --------------- | -------- | ------- | ------------------------------ |
| `sessionId`         | string          | Yes      | -       | Device Park session identifier |
| `.page(value)`      | integer         | No       | `0`     | Zero-based page index          |
| `.size(value)`      | integer         | No       | `20`    | Maximum records requested      |
| `.sortBy(value)`    | string          | No       | `ID`    | Server-side sort field         |
| `.direction(value)` | `SortDirection` | No       | `DESC`  | `ASC` or `DESC`                |
| `.addFilter(...)`   | filter rule     | No       | None    | Adds a recording filter        |

## Available Screen Record Filters

| Constant                        | Expected value   | Supported operations                              | Purpose                           |
| ------------------------------- | ---------------- | ------------------------------------------------- | --------------------------------- |
| `ScreenRecordFilter.CREATED_AT` | Timestamp string | `EQUAL`, `NOT_EQUAL`, `GREATER_THAN`, `LESS_THAN` | Filter by recording creation time |

All three SDKs transmit this filter to Device Park.

## Request Mapping

`sessionId` identifies the parent session. The list request has no body. Every SDK maps pagination, sorting and filters to query data. OAuth authorization is managed by the SDK client.

## Examples

:::CodeblockTabs
```java
ScreenRecordFilterRequest filterRequest = ScreenRecordFilterRequest.of(
        ScreenRecordFilter.CREATED_AT,
        "2026-07-28T00:00:00Z",
        SearchOperation.GREATER_THAN
);

PageDto<ScreenRecord> records = client.sessions().screenRecords(
        "session-123",
        ScreenRecordPaginationRequest.builder()
                .filters(Collections.singletonList(filterRequest))
                .page(0)
                .size(20)
                .build()
);
```

```typescript
import {
  ScreenRecordFilter,
  type ScreenRecordFilterRequest,
  ScreenRecordPaginationRequestBuilder,
  SearchOperation
} from "@device-park/public-sdk";

const filterRequest: ScreenRecordFilterRequest = {
  key: ScreenRecordFilter.CREATED_AT,
  value: "2026-07-28T00:00:00Z",
  operation: SearchOperation.GREATER_THAN
};

const records = await client.sessions().screenRecords(
  "session-123",
  new ScreenRecordPaginationRequestBuilder()
    .filters([filterRequest])
    .page(0)
    .size(20)
    .build()
);
```

```csharp
var records = await client.Sessions.ScreenRecordsAsync(
    "session-123",
    new ScreenRecordPaginationRequest.Builder()
        .AddFilter(ScreenRecordFilter.CREATED_AT, "2026-07-28T00:00:00Z", SearchOperation.GREATER_THAN)
        .Page(0)
        .Size(20)
        .Build());
```
:::

## Response Model

| Field         | Type            | Nullable | Meaning                                                                        |
| ------------- | --------------- | -------- | ------------------------------------------------------------------------------ |
| `fileKey`     | string          | Yes      | Stable identifier for one stored recording segment                             |
| `filePath`    | string          | Yes      | Server-side path of the segment, commonly an MPEG-TS (`.ts`) file              |
| `downloadUrl` | string          | Yes      | Potentially time-limited URL for this individual segment; not the HLS playlist |
| `createdAt`   | ISO-8601 string | Yes      | Creation timestamp                                                             |
| `updatedAt`   | ISO-8601 string | Yes      | Last processing update                                                         |
| `duration`    | number          | Yes      | Recording duration reported by Device Park                                     |

```json
{
  "size": 20,
  "page": 0,
  "totalPages": 1,
  "totalElements": 1,
  "data": [
    {
      "fileKey": "recording-segment-001",
      "filePath": "screen-records/session-123/recording-001.ts",
      "downloadUrl": "https://storage.example/temporary-recording-segment-url",
      "createdAt": "2026-07-28T10:00:00Z",
      "updatedAt": "2026-07-28T10:15:00Z",
      "duration": 9.5
    }
  ]
}
```

An empty successful page can mean recording processing is not complete. Retry with a bounded interval. Do not treat `downloadUrl` as permanent.

## Play the Complete Recording with HLS

When `videoRecording` is `true`, Device Park sets `Session.videoRecordUrl` after the session is closed and its recording is ready. Until then, `videoRecordUrl` can be `null`. Retrieve the session again with `client.sessions().list(...)` rather than relying on the response returned when the session was started.

The URL returns a UTF-8 HLS media playlist. It is not a single MP4 file. The playlist contains ordered media-segment URLs and durations; an HLS player loads the playlist and then requests those segments. The response is identified by the `application/vnd.apple.mpegurl` content type, so the URL does not have to end in `.m3u8`. This follows [RFC 8216](https://www.rfc-editor.org/rfc/rfc8216.html).

The SDK discovers the stream URL; it does not decode or play video. Your application must give `videoRecordUrl` to an HLS-capable player or proxy the playlist and its segments to that player. Authorization applies to both the initial playlist request and the subsequent segment requests.

### Find the HLS URL with the SDK

:::CodeblockTabs
```java
import io.testinium.devicepark.model.common.PageDto;
import io.testinium.devicepark.model.common.SearchOperation;
import io.testinium.devicepark.model.sessions.DeviceSessionFilterRequest;
import io.testinium.devicepark.model.sessions.DeviceSessionRequest;
import io.testinium.devicepark.model.sessions.Session;
import io.testinium.devicepark.model.sessions.SessionFilter;

import java.util.Collections;

DeviceSessionFilterRequest sessionFilter = DeviceSessionFilterRequest.of(
        SessionFilter.SESSION,
        "session-123",
        SearchOperation.EQUAL
);

PageDto<Session> page = client.sessions().list(
        DeviceSessionRequest.builder()
                .filters(Collections.singletonList(sessionFilter))
                .page(0)
                .size(1)
                .build()
);

Session session = page.data().stream()
        .findFirst()
        .orElseThrow(() -> new IllegalStateException("Session was not found"));

if (!"CLOSED".equals(session.state()) || session.videoRecordUrl() == null) {
    throw new IllegalStateException("The session recording is not ready yet");
}

System.out.println(session.videoRecordUrl());
```

```typescript
import {
  DeviceSessionRequestBuilder,
  type DeviceSessionFilterRequest,
  SearchOperation,
  SessionFilter
} from "@device-park/public-sdk";

const sessionFilter: DeviceSessionFilterRequest = {
  key: SessionFilter.SESSION,
  value: "session-123",
  operation: SearchOperation.EQUAL
};

const page = await client.sessions().list(
  new DeviceSessionRequestBuilder()
    .filters([sessionFilter])
    .page(0)
    .size(1)
    .build()
);

const session = page.data[0];
if (!session) {
  throw new Error("Session was not found");
}
if (session.state !== "CLOSED" || !session.videoRecordUrl) {
  throw new Error("The session recording is not ready yet");
}

console.log(session.videoRecordUrl);
```

```csharp
var page = await client.Sessions.ListAsync(
    new DeviceSessionRequest.Builder()
        .AddFilter(SessionFilter.SESSION, "session-123", SearchOperation.EQUAL)
        .Page(0)
        .Size(20)
        .Build());

var sessionId = page.Data.FirstOrDefault()?.SessionId;
```
:::

### Java: read and validate the HLS playlist

The following Java 8-compatible method consumes the URL returned by the Java SDK. Pass the `Session` obtained above and a bearer token supplied by your application's secure server-side authentication component. The Java SDK keeps its own OAuth token internal and does not expose it for use by another HTTP client.

```java
import io.testinium.devicepark.model.sessions.Session;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

static String readHlsPlaylist(Session session, String bearerToken) throws IOException {
    if (session.videoRecordUrl() == null) {
        throw new IllegalStateException("The session recording is not ready yet");
    }

    URL hlsUrl = new URL(session.videoRecordUrl());
    HttpURLConnection connection = (HttpURLConnection) hlsUrl.openConnection();
    connection.setRequestMethod("GET");
    connection.setRequestProperty("Accept", "application/vnd.apple.mpegurl");
    connection.setRequestProperty("Authorization", "Bearer " + bearerToken);

    int status = connection.getResponseCode();
    if (status != HttpURLConnection.HTTP_OK) {
        throw new IOException("HLS playlist request failed with HTTP " + status);
    }

    StringBuilder playlist = new StringBuilder();
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
        String line;
        while ((line = reader.readLine()) != null) {
            playlist.append(line).append('\n');
        }
    } finally {
        connection.disconnect();
    }

    if (playlist.indexOf("#EXTM3U") != 0) {
        throw new IOException("Device Park did not return an HLS playlist");
    }

    return playlist.toString();
}
```

This verifies and reads the RFC 8216 manifest; it does not decode the video. For playback in a Java desktop, Android or server application, pass `session.videoRecordUrl()` to an HLS-capable media library and configure that library to send the bearer token for both the manifest and every segment request. If the player cannot attach headers, expose the stream through an authenticated server-side proxy instead of placing Device Park credentials in the client application.

### Node.js: play it in a browser

[`hls.js`](https://github.com/video-dev/hls.js) is a separate browser playback dependency; it is not bundled with the Device Park SDK. The playlist and every segment request must be authorized. Obtain the bearer token through your application's secure backend or existing authenticated session. Never put the Device Park `clientSecret` in browser code.

```bash
npm install hls.js
```

```typescript
import Hls from "hls.js";
import type { Session } from "@device-park/public-sdk";

function playHlsRecording(
  session: Session,
  bearerToken: string,
  video: HTMLVideoElement
): Hls {
  if (!session.videoRecordUrl) {
    throw new Error("The session recording is not ready yet");
  }

  const hls = new Hls({
    xhrSetup(xhr) {
      // hls.js applies this header to the playlist and media-segment requests.
      xhr.setRequestHeader("Authorization", `Bearer ${bearerToken}`);
    }
  });

  hls.loadSource(session.videoRecordUrl);
  hls.attachMedia(video);
  return hls;
}

const video = document.querySelector<HTMLVideoElement>("#recording");
if (!video) {
  throw new Error("Video element was not found");
}

const bearerToken = await getBearerTokenFromYourBackend();
const hls = playHlsRecording(session, bearerToken, video);

// Call hls.destroy() when the component or page is disposed.
```

```html
<video id="recording" controls playsinline></video>
```

For a cross-origin player, the Device Park environment must allow CORS `GET` requests for both the playlist and its segment URLs. Native HLS playback through `<video src="...">` can also be used on compatible platforms, but a video element cannot attach a custom bearer header; use it only behind an authorized same-origin proxy or another access mechanism supported by your environment.

The Node.js SDK runs on the trusted server side. A browser should request a short-lived playback token or an authorized proxy URL from that server; it must not receive the `clientId` and `clientSecret` used to build `DeviceParkApiClient`.

### Operational notes

- `videoRecordUrl === null` means the stream is not available yet, recording was not enabled, or access is no longer available.
- A closed recording playlist ends with `#EXT-X-ENDLIST`. A player can reload a playlist without that tag to discover newly appended segments.
- Respect `dataAccessEndDate`; do not assume recording access is permanent.
- Use `screenRecords(...)` for segment metadata and filtering, and use `videoRecordUrl` for playback.

### Verify your integration

1. Start a session with `videoRecording(true)`, perform a short interaction and stop the session.
2. List the session until its state is `CLOSED` and `videoRecordUrl` is not `null`. Use a bounded retry interval; do not poll indefinitely.
3. Request `videoRecordUrl` with `Accept: application/vnd.apple.mpegurl` and valid authorization.
4. Confirm an HTTP `200` response, the `application/vnd.apple.mpegurl` content type and a response body beginning with `#EXTM3U`.
5. Start playback and confirm that the playlist's media-segment requests also return HTTP `200`. A successful manifest request alone does not prove that segment authorization is configured correctly.
6. Confirm that a completed recording can play to the end and that seeking works as expected.

If the manifest succeeds but the video does not start, inspect the segment requests first. HTTP `401` or `403` usually indicates that the player did not forward authorization; a browser CORS error means the Device Park environment or proxy must permit the playlist and segment origins.

## Next Step

[Release the allocation](docId\:zPik1KKbx0n9i3GXP3HoB) after all planned sessions and evidence collection are complete.
