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
| 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.
{
"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.
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 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 and seasons and 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 and rate limits.
Practical notes
- Store the chosen
siteandkeywith 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.
sizeis 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
resultscarries no meaning. Sort in your code. - For poster and backdrop files see images. For the use case view, see movie trailer API.