> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# Media Links

Frame.io stores and processes files uploaded to your projects — images, videos, PDFs, and more. When you retrieve a file via the API, the base response includes core metadata like the file name, status, and a `view_url` for opening it in the Frame.io app. To access the actual file content — the original, a preview, or different quality renditions — you use **includes**.

This guide explains the media link includes available on the **List Files** and **Get File** endpoints, what each one returns, and when to use each.

## What are includes?

Includes are optional fields you can request alongside a file response. By default the API returns only core file metadata — name, status, type, timestamps, and `view_url`. Includes let you opt in to additional data, keeping responses lean when you don't need everything.

### How to add includes

Pass a comma-separated list of include names as the `include` query parameter on any **Get File** or **List Files** request:

```bash title="Single include"
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.thumbnail
```

```bash title="Multiple includes"
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.thumbnail,media_links.original
```

```bash title="Includes on List Files"
GET /v4/accounts/{account_id}/folders/{folder_id}/files?include=media_links.thumbnail
```

Includes work the same way on both the single-file and list endpoints. When used on List Files, the includes are resolved for every file in the response.

### SDK examples

```python title="Python SDK"
import frameio

client = frameio.Frameio(auth="<YOUR_TOKEN>")

file = client.files.get(
    file_id="93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    include=["media_links.thumbnail", "media_links.original"]
)

print(file.media_links.thumbnail.url)
```

```typescript title="TypeScript SDK"
import Frameio from "frameio";

const client = new Frameio({ auth: "<YOUR_TOKEN>" });

const file = await client.files.get("93e4079d-0a8a-4bf3-96cd-e6a03c465e5e", {
  include: ["media_links.thumbnail", "media_links.original"],
});

console.log(file.media_links?.thumbnail?.url);
```

Includes that aren't requested are omitted from the response entirely.

## Media link includes overview

#### media\_links.original

The original uploaded file, exactly as it was uploaded

#### media\_links.thumbnail

A PNG preview image for display purposes

#### media\_links.high\_quality

The best available processed rendition

#### media\_links.efficient

The smallest available rendition for low-bandwidth use cases

#### media\_links.video\_h264\_180

A low-resolution 180p H264 video transcode for streaming and playback

#### media\_links.scrub\_sheet

A WebP sprite sheet of video frame thumbnails for building scrubbing UI

## media\_links.original

Returns signed URLs pointing to the **original file exactly as it was uploaded** — no processing, no conversion.

### Fields

| Field          | Type           | Description                                                                                             |
| -------------- | -------------- | ------------------------------------------------------------------------------------------------------- |
| `download_url` | string \| null | Signed URL that forces a file download (`Content-Disposition: attachment`)                              |
| `inline_url`   | string \| null | Signed URL that opens the file directly in the browser (`Content-Disposition: inline; filename=<name>`) |

### When to use

Use `media_links.original` when you need the source file — for example, to let a user download the original MOV, MP4, MXF, AVI, PSD, PNG, or TIFF, or to pass the original bytes to another system.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.original
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "hero-banner.png",
    "type": "file",
    "media_links": {
      "original": {
        "download_url": "https://s3.amazonaws.com/...",
        "inline_url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

These URLs are **temporary signed S3 URLs** — they expire. Do not cache or store them; request a fresh URL each time you need one.

## media\_links.thumbnail

Returns a **PNG preview image** of the asset, capped at 540px tall. This is a processed rendition — not the original file — and watermarks may be applied depending on your account settings.

### Fields

| Field          | Type           | Description                                                                          |
| -------------- | -------------- | ------------------------------------------------------------------------------------ |
| `download_url` | string \| null | Signed URL that forces a download of the PNG thumbnail                               |
| `url`          | string \| null | Signed URL that serves the PNG thumbnail inline with no `Content-Disposition` header |

### When to use

Use `media_links.thumbnail` when you need to **display a visual preview** of the asset — for example in a grid view, a gallery, or a file picker. It loads faster than the original and is always a web-safe PNG format.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.thumbnail
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "hero-banner.png",
    "type": "file",
    "media_links": {
      "thumbnail": {
        "download_url": "https://s3.amazonaws.com/...",
        "url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

## media\_links.high\_quality

Returns a download URL for the **best available processed rendition** of the asset. Frame.io processes uploads through a resolution ladder that maxes out at 2160p. The API returns the highest rendition available **at the time of the request** — so if only a 540p rendition has finished processing when you make the request, the API returns 540p until higher renditions are ready.

### Fields

| Field          | Type           | Description                                                            |
| -------------- | -------------- | ---------------------------------------------------------------------- |
| `download_url` | string \| null | Signed URL for the highest quality rendition available at request time |

### When to use

Use `media_links.high_quality` when you want the **best quality version without needing the original** — for example, exporting a high-res rendition for downstream processing, or presenting a full-resolution preview. If you need the highest possible quality, check the file `status` field and request this include once the file is `ready` to ensure the full resolution ladder has been processed.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.high_quality
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "hero-banner.png",
    "type": "file",
    "media_links": {
      "high_quality": {
        "download_url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

## media\_links.efficient

Returns a download URL for the **smallest available processed rendition**. This is the opposite of `high_quality` — Frame.io picks the lowest resolution rendition available.

### Fields

| Field          | Type           | Description                                          |
| -------------- | -------------- | ---------------------------------------------------- |
| `download_url` | string \| null | Signed URL for the smallest/most efficient rendition |

### When to use

Use `media_links.efficient` when **bandwidth or file size matters more than quality** — for example, generating quick previews in a low-bandwidth environment, or feeding a thumbnail pipeline that doesn't need full resolution.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.efficient
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "hero-banner.png",
    "type": "file",
    "media_links": {
      "efficient": {
        "download_url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

## media\_links.video\_h264\_180

Returns streaming and download URLs for a **low-resolution 180p H264 video transcode** of the asset.

This is a legacy include. It will be `null` if a 180p transcode was not generated for the asset. For most use cases prefer `media_links.efficient`, which selects the best available low-quality rendition rather than relying on a specific transcode existing.

### Fields

| Field          | Type           | Description                                  |
| -------------- | -------------- | -------------------------------------------- |
| `download_url` | string \| null | Signed URL to download the 180p H264 video   |
| `url`          | string \| null | Signed URL for streaming the 180p H264 video |

### When to use

Use `media_links.video_h264_180` when you need a guaranteed 180p H264 transcode specifically — for example, integrating with a legacy player or pipeline that requires this exact format. If the transcode doesn't exist for a given asset, both URLs will be `null`.

There is no audio included in the video\_h264\_180 file. It is intended to be used for very efficient previews when needed.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.video_h264_180
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "interview-clip.mp4",
    "type": "file",
    "media_links": {
      "video_h264_180": {
        "download_url": "https://s3.amazonaws.com/...",
        "url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

## media\_links.scrub\_sheet

Returns a **WebP sprite sheet** — a single image containing a grid of video frame thumbnails sampled evenly across the video duration. This is used to build video scrubbing UI, where the player shows a preview frame as the user drags across the timeline.

Scrub sheets are generated for **video assets only**. The include will return `null` URLs for images, PDFs, and other non-video files.

### Fields

| Field          | Type           | Description                                                                      |
| -------------- | -------------- | -------------------------------------------------------------------------------- |
| `download_url` | string \| null | Signed URL to download the WebP sprite sheet                                     |
| `url`          | string \| null | Signed URL to load the WebP sprite sheet inline                                  |
| `metadata`     | object \| null | Tile layout information for extracting individual frames (experimental API only) |

**`metadata` fields:**

| Field          | Type    | Description                                 |
| -------------- | ------- | ------------------------------------------- |
| `tile_x`       | integer | Number of thumbnail columns in the grid     |
| `tile_y`       | integer | Number of thumbnail rows in the grid        |
| `thumb_width`  | integer | Width of each thumbnail in pixels           |
| `thumb_height` | integer | Height of each thumbnail in pixels          |
| `padding`      | integer | Gap in pixels between thumbnails            |
| `frames`       | integer | Total number of frames sampled in the sheet |

### When to use

Use `media_links.scrub_sheet` when building a custom video player or timeline UI that needs to show a preview thumbnail as the user scrubs. Rather than making a separate request for each frame, the sprite sheet bundles all preview frames into a single image download.

### Example request

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.scrub_sheet
```

### Example response

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "interview-clip.mp4",
    "type": "file",
    "media_links": {
      "scrub_sheet": {
        "download_url": "https://assets.frame.io/...",
        "url": "https://assets.frame.io/...",
        "metadata": {
          "tile_x": 10,
          "tile_y": 10,
          "thumb_width": 160,
          "thumb_height": 90,
          "padding": 1,
          "frames": 100
        }
      }
    }
  }
}
```

### Extracting a frame from the sprite sheet

The sprite sheet is a grid of `tile_x` columns × `tile_y` rows. To display the thumbnail for a given frame index (zero-based), calculate its position within the image:

```javascript
function getFrameOffset(frameIndex, metadata) {
  const { tile_x, thumb_width, thumb_height, padding } = metadata;

  const col = frameIndex % tile_x;
  const row = Math.floor(frameIndex / tile_x);

  return {
    x: col * (thumb_width + padding),
    y: row * (thumb_height + padding),
    width: thumb_width,
    height: thumb_height,
  };
}
```

You can then use CSS `background-position` to show the correct tile from the sprite sheet:

```javascript
function applyFrameToElement(el, frameIndex, scrubSheet) {
  const { x, y, width, height } = getFrameOffset(frameIndex, scrubSheet.metadata);

  el.style.backgroundImage = `url(${scrubSheet.url})`;
  el.style.backgroundPosition = `-${x}px -${y}px`;
  el.style.width = `${width}px`;
  el.style.height = `${height}px`;
}
```

To map a playback position (in seconds) to a frame index, use the total video duration and the number of frames in the sheet:

```javascript
function positionToFrameIndex(currentSeconds, durationSeconds, metadata) {
  const progress = currentSeconds / durationSeconds;
  return Math.min(
    Math.floor(progress * metadata.frames),
    metadata.frames - 1
  );
}
```

The `metadata` field is only available on the experimental API version. On the stable v4 API, `scrub_sheet` returns `download_url` and `url` only.

## Combining multiple includes

You can request multiple includes in a single API call:

```bash
GET /v4/accounts/{account_id}/files/{file_id}?include=media_links.thumbnail,media_links.original
```

```json
{
  "data": {
    "id": "93e4079d-0a8a-4bf3-96cd-e6a03c465e5e",
    "name": "hero-banner.png",
    "type": "file",
    "media_links": {
      "thumbnail": {
        "download_url": "https://s3.amazonaws.com/...",
        "url": "https://s3.amazonaws.com/..."
      },
      "original": {
        "download_url": "https://s3.amazonaws.com/...",
        "inline_url": "https://s3.amazonaws.com/..."
      }
    }
  }
}
```

## Choosing the right include

| Goal                                           | Include to use                             |
| ---------------------------------------------- | ------------------------------------------ |
| Show a preview image in your UI                | `media_links.thumbnail`                    |
| Let a user download the original file          | `media_links.original`                     |
| Open the original file directly in the browser | `media_links.original` → `inline_url`      |
| Get the best quality rendition for export      | `media_links.high_quality`                 |
| Get a small rendition for low-bandwidth use    | `media_links.efficient`                    |
| Stream or download a low-res video (legacy)    | `media_links.video_h264_180`               |
| Build a video scrubbing UI with frame previews | `media_links.scrub_sheet`                  |
| Navigate a user to the file in Frame.io        | Use `view_url` from the base file response |

`view_url` — available on every file response without needing an include — is a permanent deep-link into the Frame.io web app. It is **not** a media URL; it opens the Frame.io UI and has no expiry. Use it when you want to direct a user to review or comment on an asset directly in Frame.io, not when you need to serve or download the file programmatically.

## Include availability

Media link includes are only populated once Frame.io has finished processing the uploaded file. If you request an include immediately after upload, the URLs may be `null` while transcoding is in progress. Check the `status` field on the file object — includes will be available once status is `ready`.