The Audiogum Platform abstracts playback of playlists, albums, audiobooks and other playable audio from various services behind a simple set of APIs. We call this concept playables
.
The starting point is creating an alias to the content that can be easily referenced from a hardware device or app. Companion apps can create these aliases for the user, that in turn can be used from a single "play" button or assigned to some form of preset if the device supports such a concept.
A playable
is the piece of data describing the parameters needed for the client to play something and potentially some state associated with playing it. The playable data is specific to a user and also locked to specific devices owned by the user.
We offer this abstraction for these reasons:
1) to simplify firmware integration with services - rather than dealing with every different service you wish to integrate, handling different API authentication schemes and response formats, Audiogum offer a consistent API that returns a list of items to play.
2) to cope with different types of content - not just playlists created by a user or service, but also to allow dynamic personal playlists that can play on any service your customer has a subscription to. In some countries, it is possible to play a playlist that sources media from multiple music services.
3) adding new services requires minimal change to your apps and firmware (typically none). With the config-driven approach, new services can light up without new releases (providing the service does not require an SDK for playback).
We offer a simple API for apps to create a playable
alias that returns an id
that can be used to assign to a preset button or some other hardware interaction on a device that signals playback should start.
The content to play is determined by the parameters
part of the playable request, as shown in the examples below. The list of supported services and types of content is growing quickly - please ask for more up to date information on features and roadmap.
In most cases, the parameters
object has the following fields:
type
: the type of content. Currently supported values are:album
playlist
song
internetradio
audiobook
dynamicplaylist
service
: the service from which the content will come (if relevant)ref
: the identifier or uri of the content according to the service (if relevant)The playable request should contain the unique deviceid
of the device that will perform the playback.
(In the case of the app performing playback, this can be omitted. See playback in app)
The following error conditions may occur:
Status | Meaning |
---|---|
403 | For services requiring a linked account, no token was found for the user - the user should sign in to proceed |
409 | User has revoked access - a previously linked account has had the access token revoked - the user should sign in again to proceed |
410 | No items found to play on service - the item may have been removed or a dynamic playlist request may be too specific to allow enough items |
499 | The service returned a custom error message - see error field in response and examples below |
599 | The service call failed or timed out - try again |
App request:
POST /v1/user/playables
{
"parameters": {
"type": "playlist",
"service": "tidal",
"ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
},
"deviceid": "X28943.12389.YURW2892"
}
Response:
The response gives the id
- this will either be a new id if the parameters have not been used before by the user, or an existing one if the parameters have been used - i.e. previously assigning the same playlist to a different device.
201 Created
{
"id": "d84af5a1c31b4fb5a186ee6671be53a9",
"name": "Headbangers Ball: 1987-1995",
"images": {
"640x640": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/640x428.jpg",
"320x320": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/320x214.jpg",
"160x160": "https://resources.tidal.com/images/a8cba84e/378e/4a99/8fd8/12d9f51da813/160x107.jpg"
},
"parameters": {
"type": "playlist",
"service": "tidal",
"ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
}
}
Playables for albums are created in the same way as playlists.
App request:
POST /v1/user/playables
{
"parameters": {
"type": "album",
"service": "napster",
"ref": "alb.54624358"
},
"deviceid": "c4:67:b5:08:14:c5"
}
Response:
201 Created
{
"id": "56d9e2db8b0b44278fa2f89a16df6ff5",
"name": "Origin Of Symmetry",
"parameters": {
"type": "album",
"service": "napster",
"ref": "alb.54624358"
},
"images": {
"default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
}
}
App request:
POST /v1/user/playables
{
"parameters": {
"type": "song",
"service": "kkbox",
"ref": "ClpUmFvYFKtg28L3dM"
},
"deviceid": "c4:67:b5:08:14:c5"
}
Response:
201 Created
{
"id": "56d9e2db8b0b44278fa2f89a16df6ff5",
"name": "最怕冷戰",
"parameters": {
"type": "song",
"service": "kkbox",
"ref": "ClpUmFvYFKtg28L3dM"
},
"images": {
"default": "https://i.kfs.io/album/tw/161877,0v3/fit/500x500.jpg"
}
}
See dynamic playlist for supported features and details of parameters
combinations.
POST /v1/user/playables
{
"parameters": {
"type": "dynamicplaylist",
"expandartists": true,
"artists": [
{
"id": "05e12a2e6206411086ffb6a48dddb64d",
"name": "Muse"
}
]
},
"deviceid": "X54322.66456.PPO38DF2"
}
Response:
201 Created
{
"id": "4605791260b4408f9683146b91b236e2",
"name": "Muse",
"images": {
"640x640": "http://artistimgs.audiogum.com/640x640/05e12a2e6206411086ffb6a48dddb64d.jpg",
"320x320": "http://artistimgs.audiogum.com/320x320/05e12a2e6206411086ffb6a48dddb64d.jpg"
},
"parameters": {
"type": "dynamicplaylist",
"variant": "artist",
"expandartists": true,
"artists": [
{
"id": "05e12a2e6206411086ffb6a48dddb64d",
"name": "Muse"
}
]
}
}
POST /v1/user/playables
{
"parameters": {
"type": "internetradio",
"service": "vtuner",
"ref": "3212"
},
"deviceid": "X54322.66456.PPO38DF2"
}
Response:
201 Created
{
"id": "4605791260b4408f9683146b91b236e2",
"name": "BBC Radio 2",
"images": {
"default": "http://logo.vtuner.net/007452/logotv/logo-3159.jpg"
},
"parameters": {
"type": "internetradio",
"service": "vtuner",
"ref": "3212"
}
}
Note about images
: where the aspect ratio and size of images are known, these are represented in the JSON, for example 320x320
. Otherwise, as in the vTuner case, when the images are of arbitrary aspect ratio, this is represented as default
.
The device will not have a user context, nor their credentials for connecting to music services. Each playable
is only accessible to specific devices that were associated with it by the user via an app, as described above. At the point of consumption of a playable
, we return media references which, dependent upon the underlying music service, may point to actual media or require an SDK integration. In the latter case, we also return a up to date credentials (e.g. a service access_token
) to pass to the local SDK.
The device does not need to know the source of the media until the point of playback. Retrieving details about the playable will return the list of items to play along with indication of how to play them:
playbackprotocol
indicates the method to play content - one of HTTP
, HTTPS
or SDK
. For SDK
, a music service SDK is required to play content - if this is the case, SDK credentials will also be provided.timelimited
indicates that content URLs are time limited to the specified number of seconds. See refreshing time-limited content URLs.playbackcodec
is returned with each item (where possible), this field shows the codec used – typical values will be mp3
, aac
, flac
, etc.total
indicates the number of items to play - for example, Internet Radio services will only return one item - a continuous stream of content.The playable details gives a list of one or more items that are individual streams to play. Each item will either have a streamurl
when the service has a playbackprotocol
of HTTP
or HTTPS
, or in the case of a service that requires an SDK, the ref
attribute can be passed to the SDK to identify the track to play. Any required SDK credentials are also supplied to initialise the SDK in the services
object of the response.
As shown in the sequence diagram above, the API is designed so that the device can get songs from a playlist a few at a time. When these are exhausted, the device should make another request to receive more and continue. The index position (startindex
) in the playlist is maintained by the Audiogum service. This simplifies the client implementation and enables the following use cases:
The same approach of making another request when the list of items has been played applies to all kinds of playable, except where there is only ever one item therefore no need to request more. One example of this is internet radio where there is only one stream. This will be indicated with a total
value of 1. Note that in other cases (for example Douban FM in China) there may be only one item in the response, but no total
in response. This indicates there may be more to request.
The API supports optionally specifying the index position to start at using the startindex
query parameter, and the number of items to return by using the itemsperpage
parameter. The current position is indicated in response by the startindex
value. Specifying a startindex
overrides the automatic continuation and sets it to the next position for a subsequent request that does not specify startindex
.
We recommend the following approach:
itemsperpage
query parameter to accept the Audiogum defaults. These are generally small to reduce the chance of expiry of access but may vary by type of playable and/or service - this avoids the need to re-request tracks or credentials.startindex
query parameter, whether starting or continuing, to make use of the automatic continuation maintained by the service, except in the cases described below.The following cases may require the device to pass the startindex
parameter:
startindex
parameter. We recommend calculating startindex
based on the current startindex
and itemsperpage
found in response, i.e. new startindex
= startindex
- itemsperpage
, or zero if greater.Note: We recommend only doing this for going backwards. For the skip forwards case we advise omitting the startindex
to make use of the automatic continuation as described above.In the case where the device specifies startindex
and/or itemsperpage
parameters, the request will fail with response code 400
if these fall out of range of the underlying playlist or currently generated dynamic playlist. In the dynamic case, the sequence is indefinite, but we don't keep infinite history or generate too far ahead. If this happens, our advice would be to try again, omitting the startindex
so that the service will give an appropriate next set of songs.
As with the previous example, each item
in the items
array returned is a song to play - however, Napster serve HTTPS progressive downloads, so a streamurl
is included in the item
that points to the stream.
Device request: GET /v1/playables/56d9e2db8b0b44278fa2f89a16df6ff5
Response:
200 OK
{
"userid": "c0cce16a3952487db2cfc08a93edb9e1",
"itemsperpage": 2,
"updated": "2017-07-03T16:35:08.764Z",
"images": {
"default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
},
"name": "Origin Of Symmetry",
"created": "2017-07-03T16:34:49.766Z",
"startindex": 0,
"total": 12,
"id": "c4a1192fe24f468db86fddde771a83da",
"items": [
{
"service": "napster",
"index": 0,
"albumname": "Origin Of Symmetry",
"ref": "tra.54624359",
"images": {
"default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
},
"playbackcodec": "AAC",
"name": "New Born",
"streamurl": "https://xxxxxx",
"type": "song",
"duration": 363,
"artistdisplayname": "Muse",
"bitrate": 320,
"artists": [
{
"type": "artist",
"service": "napster",
"ref": "art.7035",
"name": "Muse"
}
]
},
{
"service": "napster",
"index": 1,
"albumname": "Origin Of Symmetry",
"ref": "tra.54624360",
"images": {
"default": "https://direct.rhapsody.com/imageserver/v2/albums/alb.54624358/images/500x500.jpg"
},
"playbackcodec": "AAC",
"name": "Bliss",
"streamurl": "https://xxxxx",
"type": "song",
"duration": 252,
"artistdisplayname": "Muse",
"bitrate": 320,
"artists": [
{
"type": "artist",
"service": "napster",
"ref": "art.7035",
"name": "Muse"
}
]
}
],
"parameters": {
"ref": "alb.54624358",
"service": "napster",
"type": "album"
},
"services": {
"napster": {
"userid": "xxxx",
"subscription": "premium",
"playbackprotocol": "https",
"timelimited": 4440
}
}
}
In the case of dynamic playlists, there is no total
since the length is indefinite.
See dynamic playlist for supported features and details of parameters
combinations.
Device request: GET /v1/playables/25ce82bab9f24ddbb4d5863def2068b8
Response:
200 OK
{
"id": "25ce82bab9f24ddbb4d5863def2068b8",
"type": "playable",
"name": "Muse",
"images": {
"640x640": "http://artistimgs.audiogum.com/640x640/05e12a2e6206411086ffb6a48dddb64d.jpg",
"320x320": "http://artistimgs.audiogum.com/320x320/05e12a2e6206411086ffb6a48dddb64d.jpg"
},
"userid": "ab76a53c69aa4c918135399715fdf362",
"items": [
{
"id": "e185d67057a84c11b4f9f7b5ef9edc1c",
"type": "song",
"name": "Citizen Erased",
"artists": [
{
"id": "05e12a2e6206411086ffb6a48dddb64d",
"name": "Muse"
}
],
"artistdisplayname": "Muse",
"ref": "7780557",
"service": "tidal",
"streamurl": "https://..."
}
// more songs
],
"services": {
"tidal": {
"userid": "userid123",
"subscription": "premium",
"country": "gb",
"client_id": "xxxxxx",
"access_token": "xxxxxx",
"playbackprotocol": "https"
}
},
"parameters": {
"type": "dynamicplaylist",
"variant": "artist",
"expandartists": true,
"artists": [
{
"id": "05e12a2e6206411086ffb6a48dddb64d",
"name": "Muse"
}
]
},
"startindex": 0,
"itemsperpage": 5
}
For internet radio, instead of songs, the items
array contains a single internetradio
item - note that total = 1
. The item
includes the streamurl
for the audio stream.
Device request: GET /v1/playables/baa576b91981493c9652093ee7bd4e94
Response:
200 OK
{
"userid": "c0cce16a3952487db2cfc08a93edb9e1",
"images": {
"default": "https://api.audiogum.com/v1/vtuner/image?target=http%3A%2F%2Flogo.vtuner.net%2F007452%2Flogo%2Flogo-3159.jpg"
},
"name": "BBC Radio 2",
"createdtime": "2016-10-04T07:32:00.172Z",
"streamurl": "http://bbcmedia.ic.llnwd.net/stream/bbcmedia_radio2_mf_p",
"updatedtime": "2017-01-01T11:17:06.044Z",
"total": 1,
"id": "baa576b91981493c9652093ee7bd4e94",
"items": [
{
"ref": "3159",
"images": {
"default": "https://api.audiogum.com/v1/vtuner/image?target=http%3A%2F%2Flogo.vtuner.net%2F007452%2Flogo%2Flogo-3159.jpg"
},
"service": "vtuner",
"name": "BBC Radio 2",
"type": "internetradio",
"streamurl": "http://bbcmedia.ic.llnwd.net/stream/bbcmedia_radio2_mf_p"
}
],
"parameters": {
"ref": "3159",
"service": "vtuner",
"type": "internetradio"
},
"services": {
"vtuner": {
"playbackprotocol": "http"
}
}
}
For SDK music service integrations, this interaction is to cater for the case where the device gets a token expiry response from the 3rd party music service or notices that the expiry
time has been reached. This API returns a fresh token on behalf of the user. This interaction is protected by checking the deviceid
.
Device request: GET /v1/playables/25ce82bab9f24ddbb4d5863def2068b8/services/tidal
Response:
200 OK
{
"userid": "userid123",
"subscription": "premium",
"country": "gb",
"client_id": "xxxxxx",
"access_token": "xxxxxx",
"playbackprotocol": "https"
}
In the case of http(s) streaming where a value of timelimited
is provided, this value can be used to calculate when the stream URLs will expire. This could happen for example if playback is paused for some time. When resuming it may be necessary to request new stream URLs for the same items. This can be done by requesting the specific items by their ref
values as shown in the following example:
POST /v1/playables/d84af5a1c31b4fb5a186ee6671be53a9/streamurls/tidal
{
"items": [
{
"ref": "46890235"
},
{
"ref": "10220880"
}
]
}
Response:
200 OK
{
"items": [
{
"ref": "46890235",
"service": "tidal",
"streamurl": "https://..."
},
{
"ref": "10220880",
"service": "tidal",
"streamurl": "https://..."
}
]
}
Note that the playable id is provided in the path of this request so that the service can protect this interaction in the same way: it will fail if the requesting device is not associated with the playable.
Some content features exposed via Audiogum playables do not allow skipping to next and/or previous items. This could be an Internet Radio station or a service where skips are limited.
In this case the device may disable its next and previous buttons based on flags indicating this state in the playable items response.
If the field controls
is included in an item, boolean fields skipnext
and/or skipprev
with false
values indicate skipping should be disabled. If such fields are not present, the default is that the controls should be allowed.
Example playables response where both next and previous are disabled:
{
"id": "c847ff50d8ea4b158247f1e47c096d29",
"userid": "b746b99cdb99455bbd0dfcfe2c5152b4",
"name": "BBC Radio 1",
…
"items": [
{
…
"controls": {
"skipnext": false,
"skipprev": false
},
…
}
…
],
"parameters": {
…
},
"services": {
…
}
}
Longer content such as podcasts and audiobooks can have an offset
field indicating that the item should start playing at the number of seconds in.
Example of audiobook content where the user has listened to part of the content previously, leading to an offset
being sent indicating to start at 975
seconds (i.e. 16 minutes and 15 seconds
) into the stream:
{
"items":[
{
"service": "audiobooks",
"type": "audiobook",
"ref": "xxx",
"name": "...",
"images": { ... },
"streamurl": "...",
"duration": 34505000,
"offset": 975
}],
"startindex": 0,
"total": 1
}
The current position is stored with the content service automatically when the user stops the content and a stop
analytics event is received.
For many content services, Audiogum offers a shuffle feature for playables. When activated, the shuffle feature allows clients to continue playback in the usual way and let the Audiogum platform randomise the track sequence.
Before offering a shuffle button, the client should check for the presence of the shuffle
capability in the service configuration. If the service supports shuffling, the client may include "shuffle": true
when creating a playable:
POST /v1/user/playables
{
"parameters": {
"type": "playlist",
"service": "tidal",
"ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
},
"deviceid": "X28943.12389.YURW2892",
"shuffle": true
}
The user may wish to begin shuffling after they have listened to some tracks of an album or playlist. In this case, shuffle can be activated for an existing playable. Similarly, the user may decide to stop shuffling after they have listened to some shuffled tracks. In this case, shuffle can be deactivated for an existing playable:
API | Purpose |
---|---|
PUT /v1/user/playables/{id}/shuffle | Activates shuffle for a playable that is not currently shuffling |
DELETE /v1/user/playables/{id}/shuffle | Deactivates shuffle for a playable that is currently shuffling |
Note that on activating or deactivating shuffle, the client MUST tell the playback device to discard all buffered tracks. After the current track completes, the playback device should request new tracks in the normal way via GET /v1/playables/{id}
.
Where appropriate Audiogum offers a loop feature for playables. When activated, the loop feature allows clients to continue by playing the first track after the last track has played (loop all) or play a single track within a playable repeatedly (loop one).
Before offering a loop button, the client should check for the presence of the loop
capability in the service configuration. If the service supports looping, the client may include "loop": "all"
or "loop":"one"
when creating a playable:
POST /v1/user/playables
{
"parameters": {
"type": "playlist",
"service": "tidal",
"ref": "3ecfa0ed-c2aa-4a33-bd39-e33b516aa295"
},
"deviceid": "X28943.12389.YURW2892",
"loop": "all"
}
It's also possible to supply both "shuffle":true
and "loop":"all"
(or "loop":"one"
) when creating a playable, to create a playable that is both shuffled and looped. The shuffle order will remain the same with each loop.
The user may wish to begin looping after they have listened to some tracks of an album or playlist. In this case, loop can be activated for an existing playable. Similarly, the user may decide to stop looping after they have listened for some time. In this case, loop can be deactivated for an existing playable:
API | Purpose |
---|---|
PUT /v1/user/playables/{id}/loop | Activates loop for a playable that is not currently looping |
DELETE /v1/user/playables/{id}/loop | Deactivates loop for a playable that is currently looping |
Note that on activating or deactivating loop, the client MUST tell the playback device to discard all buffered tracks. After the current track completes, the playback device should request new tracks in the normal way via GET /v1/playables/{id}
.
Dynamic playlists are a special form of playable that generate a sequence of songs on-demand. They are all achieved using the API as described above, varying only by the parameters
.
Unlike a normal playlist
the sequence of songs will vary each time a dynamic playlist is generated for the same parameters. Also there is no definite length, the sequence will be continuously generated on demand.
The following variants of dynamic playlist are currently supported, with more features planned for future:
Supply a list of one or more artist ids from Audiogum's catalogue to get a dynamic playlist containing a variety of songs by those artists.
Optionally include "expandartists": true
to include recommended similar artists in the playlist.
Example parameters
object:
{
"type": "dynamicplaylist",
"expandartists": true,
"artists": [
{
"id": "05e12a2e6206411086ffb6a48dddb64d"
},
{
"id": "2a2f37d22d1c4bbdb8db21110e2772dc"
}
]
}
Supply a list of one or more music genres from Audiogum's catalogue to get a dynamic playlist containing songs from artists in those genres.
If the user has artists in those genres in their taste profile, a personalised selection of songs from those artists and recommended similar artists will be used, or otherwise from popular artists in the genres.
Example parameters
object:
{
"type": "dynamicplaylist",
"genres": [
{ "id": "indie_alternative" },
{ "id": "electronica" }
]
}
Supply neither artists nor genres to create a dynamic playlist based on the user's taste profile. This will include songs from a selection of the user's liked artists in addition to personalised recommendations for discovery of new music and re-discovery of old favourites.
Example parameters
object:
{
"type": "dynamicplaylist"
}
Taste-based radio can be optionally tuned with filters to use a subset of the user's taste. Currently genre filters are supported: supply one or more genres from the user's taste profile to filter out artists and recommendations from other genres.
Example parameters
object:
{
"type": "dynamicplaylist",
"filters": {
"genres": [
{ "id": "indie_alternative" },
{ "id": "electronica" }
]
}
}
Note that this example behaves differently to the genre radio example above (where genres are specified at the top level of parameters
rather than inside filters
):
Supply a list of user ids to create a dynamic playlist based on multiple user taste profiles. This will include songs from each user's liked artists and also recommendations selected based on suitability for the group of users - i.e. common ground.
Example parameters
object:
{
"type": "dynamicplaylist",
"users": [
{ "id": "3b2c9eeda03946e5af33d8aecfdc7a47" },
{ "id": "54c06c0cfdc2423aa40d1417096eced1" }
]
}
An example implementation of multi-user radio is available in our Friend Mix app.
See Content Services Errors for more details on error conditions.
The interactions described above assume that a companion app is controlling a connected device such as a speaker which will perform the playback.
It is possible that the app needs to do the playback itself (for example to play on a Bluetooth-only speaker). For this use case the app needs to perform both the roles, i.e. as described in both sections Creating a playable and Using a playable to start playback from device.
For convenience the app does not need to pass a deviceid
when creating a playable. When this is omitted, the service assumes the deviceid
of the device making the request, based on its authorization token, therefore the same device can request the playable for playback.