Soundtracks
GET /3/movie/{movie_id}/soundtracks and GET /3/tv/{tv_id}/soundtracks return the soundtrack album of a title in a results list. Each album has a source, name, artist, release date, link and a tracks list with disc, number, title, artist and duration. Add append_to_response=soundtracks to merge it into details.
Get the soundtrack of a movie
GET /3/movie/{movie_id}/soundtracks returns the soundtrack album of one movie. The reply carries the movie id and a results array. Each entry is an album with its details and a tracks list.
The sample loads The Dark Knight (id 155) and prints the album and one line per track.
curl -sf "https://api.amodb.dev/3/movie/155/soundtracks" \
-H "Authorization: Bearer $AMODB_KEY"const res = await fetch('https://api.amodb.dev/3/movie/155/soundtracks', {
headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` },
});
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { results } = await res.json();
if (results.length === 0) console.log('no soundtrack album known');
for (const album of results) {
console.log(`${album.album} by ${album.artist} (${album.track_count} tracks)`);
for (const t of album.tracks) {
console.log(`${t.number}. ${t.title} by ${t.artist}`);
}
}import os
import requests
r = requests.get(
"https://api.amodb.dev/3/movie/155/soundtracks",
headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
timeout=10,
)
r.raise_for_status()
results = r.json()["results"]
if not results:
print("no soundtrack album known")
for album in results:
print(f"{album['album']} by {album['artist']} ({album['track_count']} tracks)")
for t in album["tracks"]:
print(f"{t['number']}. {t['title']} by {t['artist']}")<?php
$ch = curl_init('https://api.amodb.dev/3/movie/155/soundtracks');
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");
}
$results = json_decode($body, true)['results'];
if (!$results) {
echo 'no soundtrack album known', PHP_EOL;
}
foreach ($results as $album) {
echo $album['album'], ' by ', $album['artist'] ?? 'unknown', ' (', $album['track_count'], ' tracks)', PHP_EOL;
foreach ($album['tracks'] as $t) {
echo $t['number'], '. ', $t['title'], ' by ', $t['artist'], PHP_EOL;
}
}package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Track struct {
Disc int `json:"disc"`
Number int `json:"number"`
Title string `json:"title"`
Artist string `json:"artist"`
DurationMs int `json:"duration_ms"`
}
type Album struct {
Source string `json:"source"`
Album string `json:"album"`
Artist string `json:"artist"`
Released string `json:"released"`
URL string `json:"url"`
TrackCount int `json:"track_count"`
Tracks []Track `json:"tracks"`
}
func main() {
req, _ := http.NewRequest("GET", "https://api.amodb.dev/3/movie/155/soundtracks", 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 body struct {
Results []Album `json:"results"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
panic(err)
}
if len(body.Results) == 0 {
fmt.Println("no soundtrack album known")
}
for _, a := range body.Results {
fmt.Printf("%s by %s (%d tracks)\n", a.Album, a.Artist, a.TrackCount)
for _, t := range a.Tracks {
fmt.Printf("%d. %s by %s\n", t.Number, t.Title, t.Artist)
}
}
}Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
movie_id | path | yes | Numeric movie id, for example 155. |
api_key | query | one of two | Your key. Use this or the Authorization: Bearer header. |
Response fields
| Field | Type | Notes |
|---|---|---|
id | integer | Movie id. Not present when you use append_to_response. |
results | array | Albums. Empty when no album is known. Today it holds at most one. |
results[].source | string | Where the album comes from: musicbrainz or deezer. |
results[].album | string | Album title. |
results[].artist | string or null | Album artist, for example Hans Zimmer & James Newton Howard. |
results[].released | string or null | Release date as YYYY-MM-DD. |
results[].url | string or null | Link to the album page at the source. |
results[].track_count | integer | Number of tracks on the album. |
results[].tracks | array | Tracks in album order. |
results[].tracks[].disc | integer | Disc number, starting at 1. |
results[].tracks[].number | integer | Track number on the disc. |
results[].tracks[].title | string | Track title. |
results[].tracks[].artist | string | Track artist. |
results[].tracks[].duration_ms | integer | Length in milliseconds. |
Read the keys you use by name and skip other keys.
Example response
{
"id": 155,
"results": [
{
"source": "musicbrainz",
"album": "The Dark Knight",
"artist": "Hans Zimmer & James Newton Howard",
"released": "2008-07-15",
"url": "https://musicbrainz.org/release/814b2f7b-c27c-451e-abac-a316d2e6cea9",
"track_count": 14,
"tracks": [
{ "disc": 1, "number": 1, "title": "Why So Serious?", "artist": "Hans Zimmer", "duration_ms": 554000 },
{ "disc": 1, "number": 2, "title": "I'm Not a Hero", "artist": "Hans Zimmer", "duration_ms": 395000 },
{ "disc": 1, "number": 3, "title": "Harvey Two-Face", "artist": "James Newton Howard", "duration_ms": 377000 }
]
}
]
}The track list is cut to 3 of 14. A title with no album known, such as Fight Club (id 550), answers with an empty list:
{
"id": 550,
"results": []
}Get the soundtrack of a series
GET /3/tv/{tv_id}/soundtracks returns the same shape for a series. Pass the series id, for example 1396. Breaking Bad has no album known, so its results array is empty.
| Method and path | Returns | Parameters |
|---|---|---|
GET /movie/{movie_id}/soundtracks | id and results | movie_id |
GET /tv/{tv_id}/soundtracks | id and results | tv_id |
append_to_response=soundtracks | Same results under soundtracks | Added to movie or TV details |
To get the album while loading a movie page, append it to details:
curl -sf "https://api.amodb.dev/3/movie/155?append_to_response=credits,soundtracks" \
-H "Authorization: Bearer $AMODB_KEY"The appended block sits under the soundtracks key of the record. It holds results and has no id of its own. It counts as one request.
Build a music in this movie list
Sort by disc and number, show the title and the artist, and hide the block when results is empty. The functions below return plain text lines that you can put in a list element.
function clock(ms) {
const s = Math.round(ms / 1000);
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;
}
function musicLines(results) {
const album = results[0];
if (!album) return [];
return [...album.tracks]
.sort((a, b) => a.disc - b.disc || a.number - b.number)
.map((t) => `${t.title} by ${t.artist} (${clock(t.duration_ms)})`);
}Some interface ideas that need no extra data:
| Idea | How |
|---|---|
| Numbered list | Use number as the list number, and group by disc when there are several discs. |
| Album link | Link the album name to url when it is not null. |
| Total length | Add up duration_ms over the tracks. |
| Mood pairing | Show the list beside mood meters of the same title. |
Errors
Every failure returns success as false with a numeric status_code, as TMDB does.
| HTTP | status_code | When |
|---|---|---|
| 401 | 7 | The key is missing or wrong. |
| 402 | 36 | The plan period has ended. |
| 429 | 25 | Over your requests per minute. Wait for the Retry-After seconds. |
The soundtracks endpoint does not answer 404 for an unknown id. It answers 200 with an empty results array. Appended to details, an unknown id is a 404 from the details call. See errors for every code and rate limits for the headers.
Practical notes
- Like other detail data, soundtracks are cached at the edge for 6 hours. A shorter client cache gains nothing.
- Use
append_to_response=soundtrackson a details page you already load. It saves a request of your 20, 40 or 120 requests per minute. - Do not require an album to exist. Hide the music block when
resultsis empty. - Some albums come from MusicBrainz and others from Deezer. Movie 27205 (Inception) has a Deezer album. The shape is the same, so check
sourceonly if you show it. - Track and artist names are plain strings and can contain any character. Store them as UTF-8 and escape them when you place them in HTML.
- For the trailer of the same title, see videos and trailers.