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.

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.

curl -s "https://api.amodb.dev/3/movie/550/videos" \
  -H "Authorization: Bearer $AMODB_KEY"
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');
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
$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;
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

ParameterInRequiredDescription
movie_idpathyesNumeric movie id, for example 550.
api_keyqueryone of twoYour key. Use this or the Authorization: Bearer header.
language, include_video_languagequerynoAccepted but ignored. The reply always holds every video of the title. Filter by iso_639_1 in your code.

Response fields

FieldTypeNotes
idintegerThe movie id.
resultsarrayVideo records, not sorted by type or date. Empty when none are known.
results[].iso_639_1, results[].iso_3166_1stringLanguage and country of the video, for example en and US.
results[].namestringTitle of the video, for example #TBT Trailer.
results[].keystringVideo id on that host.
results[].sitestringName of the video host. Always YouTube today.
results[].sizeintegerVertical resolution. Always 1080 today.
results[].typestringTrailer, Teaser, Clip, Featurette, Behind the Scenes, Bloopers, Opening Credits or Recap.
results[].officialbooleanTrue when the studio or rights holder published it.
results[].published_atstringISO 8601 timestamp, with or without milliseconds.
results[].idstringId 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.

TypeUse it for
TrailerThe main promo. Show this on a detail page.
TeaserA short announcement that comes before the trailer.
ClipA scene taken from the finished film or episode.
FeaturetteA short piece about how the title was made.
Behind the ScenesSet footage and interviews.
BloopersOuttakes and gag reels.
Opening CreditsThe title sequence of a series.
RecapA 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.

JavaScript
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.

Other video endpoints

The paths below return the same id and results shape.

Method and pathReturns
GET /tv/{series_id}/videosVideos of a series.
GET /tv/{series_id}/season/{season_number}/videosAlways an empty results array.
GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/videosAlways 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 and seasons and episodes.

Errors

HTTPstatus_codeWhen
4017The key is missing or wrong.
40236The plan period has ended.
40434No season or episode with that id, or an id that is not a number.
40537You used a write method on a videos path.
42925You 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 and 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. For the use case view, see movie trailer API.