# Videos and trailers API

> The videos endpoints list the trailers, teasers, clips and featurettes attached to a movie or series. Each entry gives a host name, a video key, a type, an official flag, a size and a publish date. Season and episode paths exist but return no videos. Filter by language and type in your own code.

## Endpoint

`GET /3/movie/{movie_id}/videos`

```bash
curl "https://api.amodb.dev/3/movie/550/videos" \
  -H "Authorization: Bearer $AMODB_KEY"
```

Response:

```json
{
  "id": 550,
  "results": [
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "Theatrical Trailer (HD Fan Remaster)",
      "key": "6JnN1DmbqoU",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": false,
      "published_at": "2015-02-26T03:19:25Z",
      "id": "6JnN1DmbqoU"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "#TBT Trailer",
      "key": "BdJKm16Co6M",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": true,
      "published_at": "2014-10-02T19:20:22Z",
      "id": "BdJKm16Co6M"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "20th Anniversary Trailer",
      "key": "dfeUzm6KF4g",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": true,
      "published_at": "2019-10-15T18:59:47Z",
      "id": "dfeUzm6KF4g"
    }
  ]
}
```

## Get videos for a movie

`GET /3/movie/{movie_id}/videos` returns the video records attached to one movie. A record is metadata only: a name, the host that serves the video, the video key on that host, a type and a few flags. amodb does not return video files.

The example prints the first official English trailer, falling back to any English trailer.

```bash
curl -s "https://api.amodb.dev/3/movie/550/videos" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const url = 'https://api.amodb.dev/3/movie/550/videos';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { results } = await res.json();
const trailers = results.filter((v) => v.type === 'Trailer' && v.iso_639_1 === 'en');
const pick = trailers.find((v) => v.official) ?? trailers[0];
console.log(pick ? `${pick.name} (${pick.site}, key ${pick.key})` : 'no trailer');
```

```python
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/movie/550/videos",
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
trailers = [v for v in r.json()["results"] if v["type"] == "Trailer" and v["iso_639_1"] == "en"]
official = [v for v in trailers if v["official"]]
pick = (official or trailers or [None])[0]
print(f"{pick['name']} ({pick['site']}, key {pick['key']})" if pick else "no trailer")
```

```php
<?php
$url = 'https://api.amodb.dev/3/movie/550/videos';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
    throw new RuntimeException("amodb returned $status");
}
$trailers = array_values(array_filter(
    json_decode($body, true)['results'],
    fn($v) => $v['type'] === 'Trailer' && $v['iso_639_1'] === 'en'
));
$official = array_values(array_filter($trailers, fn($v) => $v['official']));
$pick = $official[0] ?? $trailers[0] ?? null;
echo $pick ? "{$pick['name']} ({$pick['site']}, key {$pick['key']})" : 'no trailer', PHP_EOL;
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type Videos struct {
	Results []struct {
		Name     string `json:"name"`
		Key      string `json:"key"`
		Site     string `json:"site"`
		Type     string `json:"type"`
		Language string `json:"iso_639_1"`
		Official bool   `json:"official"`
	} `json:"results"`
}

func main() {
	url := "https://api.amodb.dev/3/movie/550/videos"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	client := &http.Client{Timeout: 10 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(res.Status)
	}
	var v Videos
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		panic(err)
	}
	for _, r := range v.Results {
		if r.Type == "Trailer" && r.Language == "en" && r.Official {
			fmt.Printf("%s (%s, key %s)\n", r.Name, r.Site, r.Key)
			return
		}
	}
	fmt.Println("no official trailer")
}
```

### Parameters

| Parameter                            | In    | Required   | Description                                                                                                |
| ------------------------------------ | ----- | ---------- | ---------------------------------------------------------------------------------------------------------- |
| `movie_id`                           | path  | yes        | Numeric movie id, for example `550`.                                                                       |
| `api_key`                            | query | one of two | Your key. Use this or the `Authorization: Bearer` header.                                                  |
| `language`, `include_video_language` | query | no         | Accepted but ignored. The reply always holds every video of the title. Filter by `iso_639_1` in your code. |

### Response fields

| Field                                         | Type    | Notes                                                                                                     |
| --------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `id`                                          | integer | The movie id.                                                                                             |
| `results`                                     | array   | Video records, not sorted by type or date. Empty when none are known.                                     |
| `results[].iso_639_1`, `results[].iso_3166_1` | string  | Language and country of the video, for example `en` and `US`.                                             |
| `results[].name`                              | string  | Title of the video, for example `#TBT Trailer`.                                                           |
| `results[].key`                               | string  | Video id on that host.                                                                                    |
| `results[].site`                              | string  | Name of the video host. Always `YouTube` today.                                                           |
| `results[].size`                              | integer | Vertical resolution. Always `1080` today.                                                                 |
| `results[].type`                              | string  | `Trailer`, `Teaser`, `Clip`, `Featurette`, `Behind the Scenes`, `Bloopers`, `Opening Credits` or `Recap`. |
| `results[].official`                          | boolean | True when the studio or rights holder published it.                                                       |
| `results[].published_at`                      | string  | ISO 8601 timestamp, with or without milliseconds.                                                         |
| `results[].id`                                | string  | Id of the video record. Equal to `key`.                                                                   |

### Example response

Three of the 19 records of movie 550 are shown.

```json
{
  "id": 550,
  "results": [
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "Theatrical Trailer (HD Fan Remaster)",
      "key": "6JnN1DmbqoU",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": false,
      "published_at": "2015-02-26T03:19:25Z",
      "id": "6JnN1DmbqoU"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "#TBT Trailer",
      "key": "BdJKm16Co6M",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": true,
      "published_at": "2014-10-02T19:20:22Z",
      "id": "BdJKm16Co6M"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "Mark Kermode introduces Fight Club | Film4 Interview",
      "key": "tiWCNtGDGEY",
      "site": "YouTube",
      "size": 1080,
      "type": "Featurette",
      "official": true,
      "published_at": "2015-05-08T15:59:05Z",
      "id": "tiWCNtGDGEY"
    }
  ]
}
```

## Video types

The `type` field tells you what a video is. Match it exactly, with the capital letters shown.

| Type                | Use it for                                          |
| ------------------- | --------------------------------------------------- |
| `Trailer`           | The main promo. Show this on a detail page.         |
| `Teaser`            | A short announcement that comes before the trailer. |
| `Clip`              | A scene taken from the finished film or episode.    |
| `Featurette`        | A short piece about how the title was made.         |
| `Behind the Scenes` | Set footage and interviews.                         |
| `Bloopers`          | Outtakes and gag reels.                             |
| `Opening Credits`   | The title sequence of a series.                     |
| `Recap`             | A summary of earlier episodes.                      |

Other values can appear over time, so treat an unknown `type` as a generic extra and do not fail on it.

## Pick the right video

Use the same order for every title. Try your languages in order. Inside one language, official trailers come first, then any trailer, then teasers, then clips. Within a group sort by `published_at`: ascending gives the first trailer, descending gives the latest one.

```js
function pickVideo(results, langs = ['en']) {
  const order = ['Trailer', 'Teaser', 'Clip'];
  for (const lang of langs) {
    for (const type of order) {
      const group = results
        .filter((v) => v.iso_639_1 === lang && v.type === type)
        .sort((a, b) => Number(b.official) - Number(a.official) || Date.parse(a.published_at) - Date.parse(b.published_at));
      if (group.length > 0) return group[0];
    }
  }
  return null;
}

// Russian first, English as the fallback.
// const video = pickVideo(results, ['ru', 'en']);
```

## Languages

`language` and `include_video_language` do not change the reply, so one call gives you every video of the title. Each record carries `iso_639_1` and `iso_3166_1`. Movie 550 has videos tagged `fr`, `en`, `es`, `pt`, `it`, `cs`, `pl` and `ru`. Filter by `iso_639_1` in your code as shown above, then show the user which language they are about to hear. A title can have no video in the language you want, so keep an English or any language fallback. The 14 served languages are listed on [languages](https://amodb.dev/languages/).

## Other video endpoints

The paths below return the same `id` and `results` shape.

| Method and path                                                              | Returns                          |
| ---------------------------------------------------------------------------- | -------------------------------- |
| `GET /tv/{series_id}/videos`                                                 | Videos of a series.              |
| `GET /tv/{series_id}/season/{season_number}/videos`                          | Always an empty `results` array. |
| `GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/videos` | Always an empty `results` array. |

You can also add `append_to_response=videos` to a movie or series details call. The appended block holds `results` and no `id`. Series and season pages are described in [TV series](https://amodb.dev/tv-series/) and [seasons and episodes](https://amodb.dev/tv-seasons-episodes/).

## Errors

| HTTP | status_code | When                                                                        |
| ---- | ----------- | --------------------------------------------------------------------------- |
| 401  | 7           | The key is missing or wrong.                                                |
| 402  | 36          | The plan period has ended.                                                  |
| 404  | 34          | No season or episode with that id, or an id that is not a number.           |
| 405  | 37          | You used a write method on a videos path.                                   |
| 429  | 25          | You went over your requests per minute. Wait for the `Retry-After` seconds. |

A movie or series id that does not exist is not an error here: the videos path answers 200 with `"results": []`, the same as a known title without videos. The details path answers 404 for an unknown id. More on [errors](https://amodb.dev/errors/) and [rate limits](https://amodb.dev/rate-limits/).

## Practical notes

- Store the chosen `site` and `key` with the title, not a full address, so you can change the player later.
- Video lists follow the details cache of 6 hours at the edge. A client cache of a few hours matches it.
- `size` is 1080 for every record today, so it cannot tell you which clip is light. Let the player pick the quality.
- Do not rely on one trailer existing. Hide the play button when your pick function returns null.
- The order of `results` carries no meaning. Sort in your code.
- For poster and backdrop files see [images](https://amodb.dev/images-api/). For the use case view, see [movie trailer API](https://amodb.dev/movie-trailer-api/).

## Questions and answers

### How do I get only the official trailer?

Filter the results array for type equal to Trailer and official equal to true. If several remain, sort by published_at to take the earliest or the latest one.

### How do I get videos in my language?

The endpoint returns every video of the title and ignores language and include_video_language. Filter results by iso_639_1 in your code, for example ru first and en as a fallback. Many titles have no video in a given language.

### How do I play a video from the response?

Use the site field to choose the player and the key field as the video id on that host. In the data served today site is always YouTube. Build the embed or watch address with that host's own pattern. amodb returns metadata, not video files.

### Do episodes and seasons have their own videos?

No. The season and episode videos paths answer 200 with an empty results array. Only movies and series carry video records.

## Related pages

- [Movie details API](https://amodb.dev/movies/): Movie details API with the same paths and JSON as TMDB v3. Get one movie by id, add credits, ratings and moods with append_to_response, in 14 languages.
- [TV series API](https://amodb.dev/tv-series/): TV series API with the same paths and JSON as TMDB v3. Get a show by id with seasons, networks and the next episode, plus content ratings and watch providers.
- [TV seasons and episodes API](https://amodb.dev/tv-seasons-episodes/): TV seasons and episodes API with the same paths and JSON as TMDB v3. Read a season with all its episodes, or one episode with air date, runtime and still.
- [Movie trailer API](https://amodb.dev/movie-trailer-api/): Movie trailer API on the TMDB v3 videos endpoint. Rank trailers, teasers and clips by type, official flag and language code, at one request per title.
- [Images API and image host](https://amodb.dev/images-api/): Movie images API with TMDB v3 paths: list posters, backdrops, logos and stills per title, then load them from img.amodb.dev in 10 sizes with no key.
- [Quickstart](https://amodb.dev/quickstart/): Make your first amodb call: get a key, request movie details in curl, JavaScript, Python, PHP or Go, then load the poster from img.amodb.dev. Under 2 minutes.

Updated: 2026-10-18. Source: https://amodb.dev/videos-trailers/
