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

ParameterInRequiredDescription
movie_idpathyesNumeric movie id, for example 155.
api_keyqueryone of twoYour key. Use this or the Authorization: Bearer header.

Response fields

FieldTypeNotes
idintegerMovie id. Not present when you use append_to_response.
resultsarrayAlbums. Empty when no album is known. Today it holds at most one.
results[].sourcestringWhere the album comes from: musicbrainz or deezer.
results[].albumstringAlbum title.
results[].artiststring or nullAlbum artist, for example Hans Zimmer & James Newton Howard.
results[].releasedstring or nullRelease date as YYYY-MM-DD.
results[].urlstring or nullLink to the album page at the source.
results[].track_countintegerNumber of tracks on the album.
results[].tracksarrayTracks in album order.
results[].tracks[].discintegerDisc number, starting at 1.
results[].tracks[].numberintegerTrack number on the disc.
results[].tracks[].titlestringTrack title.
results[].tracks[].artiststringTrack artist.
results[].tracks[].duration_msintegerLength in milliseconds.

Read the keys you use by name and skip other keys.

Example response

JSON
{
  "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:

JSON
{
  "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 pathReturnsParameters
GET /movie/{movie_id}/soundtracksid and resultsmovie_id
GET /tv/{tv_id}/soundtracksid and resultstv_id
append_to_response=soundtracksSame results under soundtracksAdded to movie or TV details

To get the album while loading a movie page, append it to details:

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

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

IdeaHow
Numbered listUse number as the list number, and group by disc when there are several discs.
Album linkLink the album name to url when it is not null.
Total lengthAdd up duration_ms over the tracks.
Mood pairingShow the list beside mood meters of the same title.

Errors

Every failure returns success as false with a numeric status_code, as TMDB does.

HTTPstatus_codeWhen
4017The key is missing or wrong.
40236The plan period has ended.
42925Over 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=soundtracks on 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 results is empty.
  • Some albums come from MusicBrainz and others from Deezer. Movie 27205 (Inception) has a Deezer album. The shape is the same, so check source only 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.