API Endpoints

The Rhino Engine is a RESTFUL service returning a JSON object as the result of a query.

There are three API endpoints available: Track, Release, and Playlist.

The Rhino Engine intelligently matches data from nine streaming/digital retail services: Spotify, Xbox Music, Rdio, Google Play, Beats Music, Deezer, Youtube, Amazon, and iTunes, using a custom, Rhino hosted Music Brainz server to provide a central ID to reference tracks and albums.

Track

Track queries come in two types:


• Track Search

Track Search returns a list of tracks found based on the search string along with matched data from available music services. An example track search:

http://engine.rhinopop.com/api/search/track/paranoid android

- returns an array of track objects containing basic track info as well as matched results from available music services. No need to worry about spaces in the search string, they are automatically handled. The Rhino Engine also accepts search terms seperated by a "+". So "paranoid+android" would also be acceptable as a search string.


• Track Lookup

Track Lookup returns a single track object with matched data from available music services. The Rhino Engine uses a custom, Rhino hosted, Music Brainz server instance to normalize results and provide a central, unique, ID to reference a specific track and it's related music service data. Music Brainz uses unique IDs for both tracks and releases. So a track lookup using the Rhino Engine API would look like this:

http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29

You'll notice the query consists of two Music Brainz IDs separated by a pipe. The first ID is the actual track ID, the second ID is the release that this particular track resides on. We do this because of course a track with the same ISRC can reside on multiple releases. This way, we do our best to ensure that the data returned with the track is in reference to a specific release. For example, we might want to reference a track on a particular best of compilation rather than it's original studio release. TLDR, here we are looking up a specific track on a specific release. -In this case we are asking for "Paranoid Android" off of the release "OK Computer" by Radiohead.

Release (Album)

Release queries also come in two types:


• Release Search

Release Search returns a list of releases found based on the search string. An example release search:

http://engine.rhinopop.com/api/search/release/automatic for the people

- returns an array of release objects containing basic release info.
*Because looking up individual tracks is very expensive and time consuming, release search data is limited to the basic release data. From there we can do a specific release lookup to retrieve the detailed individual track data from all available services.


• Release Lookup

Release Lookup returns a single release object with matched data from available music services. Again, the Rhino Engine uses a custom, Rhino hosted Music Brainz server instance to normalize results and provide a central, unique, ID to reference a specific release and it's related music service data. So a release lookup using the Rhino Engine API would look like this:

http://engine.rhinopop.com/api/lookup/release/id/3ebe514d-d197-3984-81ee-4d09231be775

Here we're looking up the specific release "Automatic For The People" by R.E.M. We only need the unique ID of the release in this case.

Artist

Artist queries also come in two types:


• Artist Search

Artist Search returns a list of artists and their discographies found based on the search string. An example artist search:

http://engine.rhinopop.com/api/search/artist/grateful dead
*Because looking up individual tracks is very expensive and time consuming, artist search data is limited to the a list of releases. From there we can do a specific release lookup to retrieve the detailed individual track data from all available services.


• Artist Lookup

Artist Lookup returns a single artist object (in contrast to search, which will return multiple matches).

http://engine.rhinopop.com/api/lookup/artist/id/6faa7ca7-0d99-4a5e-bfa6-1fd5037520c6

Here we're looking up the specific artist, Grateful Dead. We only need the unique ID of the artist in this case.

Playlist

The Rhino Engine is not passive, but in fact two way street. In addition to being able to lookup and search data concerning releases and tracks across all music services, it can also "push" playlists to multiple services at once. What does this mean? With some some trickery and sometimes unconventional methods, we can programatically publish actual playlists to an account at multiple music services simultaneously. Essentially, gone is the black hole of manually duplicating a playlist in each service. - One link generated for one playlist at multiple services. The Rhino.com CMS has the ability to create and store a playlist (using our track and release IDs as a reference) and at the same time push that playlist to multiple services. Currently this can be accomplished automatically for Spotify, Rdio, and Google Play. We also know that this is technically possible for Deezer and Beats as well, and plans are in the works to expand support to those services. At this point in time, the Playlist endpoint is limited to simply recalling said playlist content type data from the Rhino.com CMS that has been previously created. In the future we plan on implementing a Playlist Search, that for example poles Rhino.com for playlists tagged "80s" or perhaps by an artist name.


• Playlist Lookup

Playlist Lookup returns a single Rhino.com playlist object with and array of tracks and their associated music service data. An example Playlist search:

http://engine.rhinopop.com/api/lookup/playlist/id/uevzj8ji

- returns a single playlist object with basic info, as well as an array of track objects, each of which includes info from available music services. Rhino.com automatically creates a unique ID for each playlist created in the CMS. In this case we are retrieving data used for the "Poptopia" playlist player featured on the application examples page.

Additional Parameters

The Rhino Engine also makes available a few additional parameters to suite specific application and development needs.


• JSONP Callback

Any Rhino Engine endpoint can also be asked to return the data wrapped in a callback -with results you've come to expect. An example Track Lookup wrapped in a callback:

http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29?callback=?

And as you can see, our track data is wrapped in a "?". And we're all set for cross-domain JSONP javascript requests.


• Pretty Print

Likely the most useful API tool this page - the Rhino Engine API can return "pretty" formatted JSON to the browser for human friendly readability.

http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29?pretty

And we have our "Paranoid Android" track data displayed in a nicely formated JSON tree. Note that you can not actually use this as input for an application since it is actually html being returned to the browser with normal http headers wrapped in "pre" tags.


• Clear Cache

On any first request the Rhino Engine automatically caches and stores said result for any endpoint in a JSON blob locally for fast delivery in the future. There is no lifetime on the cache, services rarely change their data structure or URLs (for obvious reasons). But in the case that this does occur, we can request that the Rhino Engine rebuild the data and cache from scratch and re-query every music service. An example Track Lookup request with a new cache build:

http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29?clearcache

And here we have our "Paranoid Android" track again with fresh data, and freshly populated cache. Be warned that building tracks is expensive on both time and CPU. NEVER skip the cache in a production environment.


• Multiple Simultanious Lookups

Or a "playlist on the fly". The Rhino Engine is capable of returning results of mulitple track lookups in one request. (only available for track lookups currently). A track lookup example with multiple tracks:

http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29,a7581aff-fda9-40bd-85f0-e01aaa612b2e|0d56cbb7-50ee-4539-88b3-d338292b61ab,a0ca0cc0-cc0d-431c-8a36-dd917ea0dfa2|0bd09465-232e-4889-b866-eed92b4e4ec5

You'll notice that the query here is very similar to our usual track lookup - a track ID paired with a release ID via a pipe. The difference is that we passed multiple track/release pairs separted by commas. In this particular case we get back a "pseudo playlist" on the fly of three tracks returning: "Paranoid Android", "No Fun", and "Ziggy Stardest", in an array of track objects.

Data Structure

The Rhino Engine tries to maintain a consistent data structure and expected sub-structures. The object returned as the result of a request will always consist of a "tracks" or "releases" array or a "playlist" object and a cachelife timestamp.

Regardless of the request type "search" or "lookup", tracks will be wrapped in a "tracks" array, and releases will be wrapped in a "releases" array. The difference being that a single "track" or "release" lookup will always result in an array length of 1.


Tracks

The data structure for a single track lookup, or the first track in array of tracks as the result of a track search would look like this:

Object => tracks[0]

Releases (albums)

The data structure for a single release lookup, or the first release in array of releases as the result of a release search would look like this:

Object => releases[0]

As previously mentioned, we have tried to maintain sub-data structures in a consistent, predictable way. For example, the data for all the tracks under a "release" lookup is formatted identically to a "track" search or lookup. So the data for the first track of the first release in a "release" search or lookup would look like this:

Object => releases[0] => tracks[0]

Same goes for the first track in a "playlist":

Object => tracks[0]

Services (Streaming and Digital Retail Partners)

The best matched data found at a "service" is attached to the main item in a sub object. For example the iTunes data for a "track" "lookup" would be located here:

Object => tracks[0] => services => itunes

And similar is the structure for a "release":

Object => releases[0] => services => itunes

And as you might have guessed by now... the first "track" itunes data for the first release in a "release" "lookup" or "search":

Object => releases[0] => tracks[0] => services => itunes

Services Data Structures

Currently the Rhino Engine supports search and lookup at nine streaming/digital retail services: Spotify, Xbox Music, Rdio, Google Play, Beats Music, Deezer, Youtube, Amazon, and iTunes.

Originaly we thought that we should normalize the key names of results from the various services. But the data available and the context varies so wildly from service to service, that this does not often make sense. What if a service adds a new piece of data? Do we return a track "URI" from Spotify as is the default in it's API, or do we convert it to a web "URL" to match the other service links? The number of services the Rhino Engine supports (9 and counting), and the amount of data to map to each of these services becomes impossible to maintain, not to mention document. And it's only going to get worse. All the services have well documented APIs, let's use them. This allows our data to be flexible and instantly available.

Code Examples

Below You'll find a few code examples in Javascript and PHP to help get you started using the Rhino Engine.


Javascript: Track lookup - cross domain Jquery JSONP callback:

$(document).ready(function ($)
{
  $.ajax(
  {
    type: "GET",
    url: 'http://engine.rhinopop.com/api/lookup/track/id/9f9cf187-d6f9-437f-9d98-d59cdbd52757|0b6b4ba0-d36f-47bd-b4ea-6a5b91842d29',
    format: "jsonp",
    dataType: 'jsonp',
    accepts:
    {
      text: "application/json"
    },
    jsonp: 'callback'
  })
  .done(function(data)
  {
    console.log(data); //Object {tracks: Array[1], timestamp: "04236", timer: 6.2830078601837}
  });
});

In this example we passed the track ID for "Paranoid Android" as the first part of the request followed by a pipe and then finally the ID for the release "OK Computer". Download this example


PHP: Release lookup - using file_get_contents():

$release = file_get_contents('http://engine.rhinopop.com/api/lookup/release/id/14332703-ee47-4cba-b07b-af3486774648');
$release = utf8_encode($release);
$release = json_decode($release,true);
print_r($release); //Array ( [releases] => Array ( [0] => Array ( [title] => Array ( [name] => Automatic ...

And in this example we passed the ID for the release "Automatic for the People" by "R.E.M.". Download this example
*Remember to always sanitize your results encoding by converting to utf8 by default just to be sure.
*Keep in mind, that while file_get_contents() is easy to implement, it's incredibly slow comparitively. So for say, an example playlist application, that requires dozens and dozens of lookups, you might want to roll your own CURL function or look into a PHP/HTTP client framework such as Guzzle.


Player Code: And finally, if you really want to dig in, download theRhino Engineplayer source code.

The Rhino Engine's embeddable player is mostly wrtten in PHP with some clever javascript hacks to determine a user's device type (iOS, Android, desktop browser version, etc.), and also setting cookies for tracking and saving a user's preferences, as well as utilizing the Rhino.com location code API to determine a visitor's country. Download the player here