Media Assets API#
Read one asset from your tenant's Media Libraries: its file, its library, its metadata, and the plays it shows where its source declared them. Each response carries the same version the content changes feed reports for the asset, so you can tell whether you have already handled this state of it.
Endpoint Summary#
| Endpoint | Description |
|---|---|
GET /api/media-assets/by-id/{id} |
One Media Library asset by its id, with its version |
Get a Media Asset (GET /api/media-assets/by-id/{id})#
GET /api/media-assets/by-id/{id}
x-storyteller-api-key: your-api-key-here
id is the asset's id, as a mediaAsset item in the content changes feed gives it. The asset must be in your tenant, which the API key decides.
Successful responses carry Cache-Control: private, no-store.
Example Response#
A file uploaded in Studio while editing a Story:
{
"id": "33333333-3333-4333-8333-333333333333",
"version": "00000000002C9A57",
"library": {
"id": "44444444-4444-4444-8444-444444444444",
"providerCode": "MEDIA"
},
"title": "Brown dunk",
"fileUrl": "https://cdn.example.com/assets/studio-uploads/3333/source.mp4",
"thumbnailUrl": "https://cdn.example.com/assets/studio-uploads/3333/source.jpg",
"mimeType": "video/mp4",
"width": 1080,
"height": 1920,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"createdAt": "2026-10-20T23:50:00Z",
"metadata": [
{ "name": "filetype", "value": "video/mp4" },
{ "name": "height", "value": "1920" },
{ "name": "sha256", "value": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" },
{ "name": "studioPageId", "value": "66666666-6666-4666-8666-666666666666" },
{ "name": "studioStoryId", "value": "55555555-5555-4555-8555-555555555555" },
{ "name": "title", "value": "Brown dunk" },
{ "name": "width", "value": "1080" }
],
"source": {
"kind": "studioUpload",
"sourceAssetId": null,
"revision": null,
"rendition": "9:16",
"events": [],
"uploadKeys": ["tenants/77777777-7777-4777-8777-777777777777/2026/295/upload/source.mp4"]
},
"uploadedFor": {
"storyId": "55555555-5555-4555-8555-555555555555",
"clipId": null,
"pageId": "66666666-6666-4666-8666-666666666666"
},
"legacyMediaExternalId": null
}
An asset a media provider delivered reads source.kind provider, with the provider's own id as source.sourceAssetId and, where the provider declared them, the plays it shows in source.events:
"events": [
{
"game": { "provider": "stats-feed", "id": "game-1001" },
"play": { "provider": "stats-feed", "id": "11" },
"actionType": "Dunk",
"period": 2,
"gameClock": "7:34",
"eventTimeUtc": "2026-10-20T23:45:12Z"
}
]
Response Fields#
| Field | Type | Description |
|---|---|---|
id |
string | The asset's id |
version |
string or null | The version the content changes feed reports for this state of the asset. Opaque: compare it for equality. Null if the asset has not been recorded in the feed. It moves when anything below changes, except the URLs (see fileUrl). |
library |
object or null | The Media Library the asset is in: its id, and providerCode, the library's provider, such as MEDIA. Null if it is in none. |
title |
string or null | The asset's title |
fileUrl |
string or null | The file's URL. Built with your tenant's CDN address when you read it, so a change of CDN address changes it without changing version. |
thumbnailUrl |
string or null | The thumbnail's URL, where the asset has one, built as fileUrl is |
mimeType |
string or null | The file's MIME type, where Storyteller holds it |
width, height |
integer or null | The file's dimensions in pixels, where Storyteller holds them |
sha256 |
string or null | The file's SHA-256, where Storyteller holds it (Studio uploads) |
createdAt |
string | When the asset was added, UTC |
metadata |
array | Every metadata entry on the asset, as name and value, in name order. A duration entry, where present, is in the unit its source used. |
source.kind |
string | provider when Storyteller holds a media provider's own id for the asset (source.sourceAssetId); studioUpload when the asset's metadata carries the record Studio keeps for a file uploaded while editing a Story or Clip (its sha256 and the studioStoryId or studioClipId); otherwise other. Both are read from the asset's own metadata, so an import that writes the same entries reads the same. An asset synced from a provider whose id Storyteller does not hold this way reads other. Treat an unknown value as other. |
source.sourceAssetId |
string or null | The provider's own id for the asset |
source.revision |
string or null | Reserved for the provider's revision of the asset; null for now |
source.rendition |
string or null | The aspect ratio in lowest terms, such as 9:16; null without both dimensions |
source.events |
array | The plays the asset shows, as its source declared them, in order: game and play (each provider and id, or null), actionType, period, gameClock (minutes and seconds left, such as 7:34) and eventTimeUtc. Ids and providers are exactly as the source gave them: an event whose id or provider is longer than Storyteller keeps (256 characters for a play id) is left out rather than shortened, and a longer actionType or gameClock reads null. At most 200 are kept. Empty when the source declared none, and for assets added before Storyteller kept them. These are what the source supplied: the league's own data is authoritative. |
source.uploadKeys |
array of strings | The storage keys of the Studio uploads this asset holds, in ordinal order: the file it was captured from, and each later upload of the same file, which adds its key rather than a new asset. A Studio page or Clip that uses an upload refers to it by this key. Keys are only added, and adding one is a change to the asset. The list holds every key at the asset's version, and may also hold keys added since, each of which arrives as its own later change. Empty when there are none, including for assets captured before keys were recorded. |
uploadedFor |
object or null | For a Studio upload, the storyId or clipId, and the pageId, it was uploaded for; otherwise null. Uploading a file that is already in your Studio uploads library adds no new asset, so uploadedFor keeps the Story or Clip it was first uploaded for. |
legacyMediaExternalId |
string or null | The id Storyteller's earlier media delivery gave this asset, so a system that already holds it can find it; null if it was never delivered that way |
New fields may be added; ignore any you do not recognise.
Error Handling#
All errors are RFC 7807 Problem Details.
404 Not Found when the asset does not exist, has been deleted, or is not in your tenant:
{
"status": 404,
"type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
"title": "Not Found",
"detail": "Media asset with id 33333333-3333-4333-8333-333333333333 not found",
"instance": ""
}
A mediaAsset item that the feed lists as deleted reads as 404.
401 Unauthorized without a valid Server App key (see Authentication).
Related Documentation#
- Content Changes API – which assets changed, and their versions
- Plays API – a play by
id - Authentication – Required header reference