The tuneefy API

Welcome to the tuneefy API. This API provides access to the tuneefy.com service and exposes endpoints to search, aggregate and share music links.


Version

The current version is v2, which this documentation is for. The legacy API is available at https://api.tuneefy.com but will be deprecated soon. Next versions of the API will reside in the same host, and will be prefixed with, say v3, v4, … for instance.


Notational Conventions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC2119.


Representation of Date and Time

All exchange of date and time-related data, if applicable, MUST be done according to ISO 8601 standard and stored in UTC.

When returning date and time-related data, the YYYY-MM-DDThh:mm:ss+P format MUST be used.


Rate limiting

You SHOULD NOT hit the API more than 10 times per second. For the agreggate method, the limit is ~10 requests per minute. If you make too many requests, your IP might be blocked without warning. Contact us if this occurs, to find a solution.

This is currently a limitation of the server on which the tuneefy API resides. Remember that this project is provided as-is, and that the costs (server, bandwidth, maintenance) are only covered by donations (that, as of 2018 and from the start, account for a mere €12).


Authentication

The API endpoints require an OAuth access token. The token is necessary to authenticate all requests to the API.


OAuth

The tuneefy API currently supports the OAuth 2 draft specification. All OAuth2 requests MUST use the SSL endpoint available at https://data.tuneefy.com/v2/.

OAuth 2.0 is a simple and secure authentication mechanism. It allows applications to acquire an access token for tuneefy via a POST request to a token endpoint. Authentication with OAuth can be accomplished in the following steps:

  1. Register for an API key by sending a mail to api@tuneefy.com

  2. Exchange your customer id and secret for an access token

  3. Make requests by passing the token in the Authorization header

  4. When your token expires, you can get a new one


Getting an authorization token

Use the /auth/token endpoint to get a valid token. You will need your consumer key and secret.

The only accepted grant type is client_credentials. See further down for more info.


Passing the authorization token

You can either use the Authorization header, as such :

Authorization: bearer 5262d64b892e8d4341000001

Or you can pass the access_token parameter with all your requests.

GET platforms?access_token=5262d64b892e8d4341000001

The header method is RECOMMENDED.

In short

  • This API uses OAuth2 for authentication

  • A token MUST be retrieved from the authentication endpoint before issuing any request

  • Token MUST be provided in a Authorization header, or via the access_token parameter

  • Token MUST be provided for each request


Pagination

The API can limit the number of results returned (for search and aggregate methods) via the limit parameter.

GET search/track/qobuz?q=chopin&limit=2

This is not strictly speaking pagination since there is no way to pass an offset to the API to retrieve the subsequent results, are they are fetched from different platforms that do not all support pagination.

The default limit is 10 results.


Response format

This API uses the Accept header to identify the data type it should return.

Accept: application/json

This header SHOULD be present in every request. If not, the API will use the default response format.

Alternatively, all API methods support an optional return format parameter format=json. Note that html is the default response format, but xml and json are also available.

Return type Accept header format parameter
JSON Accept: application/json format=json
XML Accept: application/xml format=xml
XML (alt) Accept: text/xml format=xml
HTML Accept: text/html format=html

Info

The API does not support JSONP.


Status Codes and Errors

Status codes

This API uses HTTP status codes to communicate with the API consumer.

  • 200 OK - Response to a successful GET or POST.

  • 400 Bad Request - Malformed request; form validation errors.

  • 401 Unauthorized - When no or invalid authentication details are provided.

  • 404 Not Found - When a non-existent resource is requested.

  • 405 Method Not Allowed - Method not allowed.

  • 406 Not Acceptable - Could not satisfy the request Accept header.


Error codes and responses

This API returns both machine-readable error codes and human-readable error messages in response body when an error is encountered. An error can be encountered even if the response HTTP status code is 200.

  • GENERAL_ERROR - An error was encountered

  • BAD_PLATFORM_TYPE - This type of platform does not exist

  • BAD_PLATFORM - This platform does not exist

  • MISSING_PERMALINK - Missing or empty parameter : q (permalink)

  • PERMALINK_UNKNOWN - This permalink does not belong to any known platform

  • FETCH_PROBLEM - There was a problem while fetching data from the platform

  • FETCH_PROBLEMS - There was a problem while fetching data from the platforms

  • NO_MATCH - No match was found for this search

  • NO_MATCH_PERMALINK - No match was found for this permalink

  • MISSING_QUERY - Missing or empty parameter : q (query)

  • BAD_MUSICAL_TYPE - This musical type does not exist

  • NO_INTENT - Missing or empty parameter : intent

  • NOT_CAPABLE_TRACKS - This platform is not capable of searching tracks

  • NOT_CAPABLE_ALBUMS - This platform is not capable of searching albums

  • NOT_AUTHORIZED - Not authorized, check the token


Intents

When searching on the tuneefy API, results are returned with an intent code, in the share object :

{
    ...
    share: {
        intent: '5911b14860e96',
        expires: '2016-05-05T12:30:00'
    }
}

This means that each result can be shared easily until the intent expires.

On our side, this allows for a practical way to create tuneefy link without cluttering the database and creating many links for each search result. As well, you don’t necessarily need a tuneefy link when using the tuneefy API, hence the design choice.

To get the tuneefy link from the intent, just call the /share endpoint with this intent. It will consume the intent and return the tuneefy link corresponding to the search result.

Once an intent is consumed, it is no longer available and calling the /share with a consumed or expired intent will result in a 400.

Authentication

OAuth Access token

A valid token MUST be provided for each request that requires authentication.

POST https://data.tuneefy.com/v2/auth/token
RequestsGet a tokenMissing info
Headers
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Body
client_id=Your application customer key
&client_secret=Your application customer secret
&grant_type=client_credentials
Responses200400
Headers
Content-Type: application/json
Body
{
  "access_token": "5262d64b892e8d4341000001",
  "scope": "all",
  "expires_in": 3600,
  "token_type": "Bearer"
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "access_token": {
      "type": "string",
      "description": "valid Token"
    },
    "scope": {
      "type": "string",
      "description": "scopes of current token"
    },
    "expires_in": {
      "type": "number"
    },
    "token_type": {
      "type": "string"
    }
  }
}
Headers
Content-Type: application/json
Body
{
  "error": "invalid_client",
  "error_description": "The client credentials are invalid"
}
Headers
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Body
client_id=Your application customer key
&client_secret=Your application customer secret
Responses400
Headers
Content-Type: application/json
Body
{
  "error": "invalid_request",
  "error_description": "The grant type was not specified in the request"
}

Get an access token
POST/auth/token

Allows to retrieve a valid OAuth token for your application, using the application consumer key and secret.

This endpoint is not protected


Platforms

Platforms list

GET https://data.tuneefy.com/v2/platforms?type=streaming
RequestsValid AuthenticationInvalid token
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200404
Headers
Content-Type: application/json
Body
{
  "platforms": [
    {
      "name": "Deezer",
      "type": "streaming",
      "homepage": "https://www.deezer.com/",
      "tag": "deezer",
      "mainAccentColor": "181818",
      "enabled": {
        "api": true,
        "website": true
      },
      "capabilities": {
        "track_search": true,
        "album_search": true,
        "lookup": true
      }
    }
  ]
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "platforms": {
      "type": "array"
    }
  }
}
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "BAD_PLATFORM": "This platform type does not exist"
    }
  ]
}
Headers
Accept: "application/json"
Authorization: "Bearer invalid_token"
Responses401
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "NOT_AUTHORIZED": "Not authorized"
    }
  ]
}

Retrieve all Platforms
GET/platforms{?type}

Retrieves the list of available Platforms

List of available types

Type Underlying interface
streaming Platform\WebStreamingPlatformInterface
store Platform\WebStoreInterface
scrobbling Platform\ScrobblingPlatformInterface
URI Parameters
HideShow
type
string (optional) Example: streaming

Platform detail

GET https://data.tuneefy.com/v2/platform/deezer
RequestsExample
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200404
Headers
Content-Type: application/json
Body
{
  "name": "Deezer",
  "type": "streaming",
  "homepage": "https://www.deezer.com/",
  "tag": "deezer",
  "mainAccentColor": "181818",
  "enabled": {
    "api": true,
    "website": true
  },
  "capabilities": {
    "track_search": true,
    "album_search": true,
    "lookup": true
  }
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "type": {
      "type": "string"
    },
    "homepage": {
      "type": "string"
    },
    "tag": {
      "type": "string"
    },
    "mainAccentColor": {
      "type": "string"
    },
    "enabled": {
      "type": "object",
      "properties": {
        "api": {
          "type": "boolean"
        },
        "website": {
          "type": "boolean"
        }
      }
    },
    "capabilities": {
      "type": "object",
      "properties": {
        "track_search": {
          "type": "boolean"
        },
        "album_search": {
          "type": "boolean"
        },
        "lookup": {
          "type": "boolean"
        }
      }
    }
  }
}
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "BAD_PLATFORM": "This platform does not exist"
    }
  ]
}

Retrieve a Platform
GET/platform/{tag}

Retrieves a specific Platform and its details

See /platforms to get a list of all available platforms

URI Parameters
HideShow
tag
string (required) Example: deezer

GET https://data.tuneefy.com/v2/search/track/deezer?q=ok+computer&mode=lazy
RequestsTrackAlbumWithout `q` parameter
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "results": [
    {
      "musical_entity": {
        "type": "track",
        "title": "Karma Police",
        "album": {
          "title": "OK Computer",
          "artist": "Radiohead",
          "picture": "https://api.deezer.com/album/14879699/image",
          "safe_title": "OK Computer",
          "extra_info": {},
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": [
            "Deluxe Edition"
          ]
        },
        "links": {
          "deezer": [
            "http://www.deezer.com/track/138539981"
          ]
        },
        "safe_title": "Karma Police",
        "extra_info": {
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": []
        }
      },
      "metadata": {
        "score": 0.97
      },
      "share": {
        "intent": "590c6c8320b1b"
      }
    }
  ]
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "results": {
      "type": "array"
    }
  }
}
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "results": [
    {
      "musical_entity": {
        "type": "album",
        "title": "Ok Computer",
        "artist": "Radiohead",
        "picture": "https://api.deezer.com/album/14879699/image",
        "links": {
          "deezer": [
            "https://www.deezer.com/album/14879699"
          ]
        },
        "safe_title": "Ok Computer",
        "extra_info": {
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": []
        }
      },
      "metadata": {
        "score": 1
      },
      "share": {
        "intent": "590c6c8320b1b",
        "expires": "2016-05-05T12:30:00"
      }
    }
  ]
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "results": {
      "type": "array"
    }
  }
}
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "MISSING_QUERY": "Missing or empty parameter : q (query)"
    }
  ]
}

Search a specific platform
GET/search/{type}/{tag}{?q,mode}

Search for tracks or albums on a specific platform

Capabilities

To be able to search on a platform, the platform must have the track_search or album_search capability.

Mode

The mode indicates whether we’re going to eagerly fetch data when it’s missing from the platform response. It is not used as for now but was handy when Spotify didn’t return the artist when fetching an album. In eager mode, we made a supplementary call to get the artist info.

Don’t forget to url-encode the query (q) if necessary

URI Parameters
HideShow
type
string (required) Example: track
tag
string (required) Example: deezer
q
string (required) Example: ok+computer
mode
string (optional) Example: lazy

Lookup

GET https://data.tuneefy.com/v2/lookup?q=https:/open.spotify.com/track/6IWxHgVbdKyUMydhVGXRaT&mode=lazy
RequestsExampleUnknown permalink
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "result": {
    "musical_entity": {
      "type": "track",
      "title": "On The Road Again",
      "album": {
        "title": "The Best Of Canned Heat",
        "artist": "Canned Heat",
        "picture": "https://i.scdn.co/image/b83f79c17a44a9aa24a49748d489c52d80c36fb5",
        "safe_title": "The Best Of Canned Heat",
        "extra_info": {},
        "is_cover": false,
        "is_remix": false,
        "acoustic": false,
        "context": []
      },
      "links": {
        "spotify": [
          "https://open.spotify.com/track/6IWxHgVbdKyUMydhVGXRaT"
        ]
      },
      "safe_title": "On The Road Again",
      "extra_info": {
        "is_cover": false,
        "is_remix": false,
        "acoustic": false,
        "context": []
      }
    },
    "metadata": {
      "query_words": [
        "Canned Heat",
        "On The Road Again"
      ],
      "platform": "Spotify"
    },
    "share": {
      "intent": "590c6c8320b1b",
      "expires": "2016-05-05T12:30:00"
    }
  }
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "result": {
      "type": "object",
      "properties": {
        "musical_entity": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string"
            },
            "title": {
              "type": "string"
            },
            "album": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "artist": {
                  "type": "string"
                },
                "picture": {
                  "type": "string"
                },
                "safe_title": {
                  "type": "string"
                },
                "extra_info": {
                  "type": "object",
                  "properties": {}
                },
                "is_cover": {
                  "type": "boolean"
                },
                "is_remix": {
                  "type": "boolean"
                },
                "acoustic": {
                  "type": "boolean"
                },
                "context": {
                  "type": "array"
                }
              }
            },
            "links": {
              "type": "object",
              "properties": {
                "spotify": {
                  "type": "array"
                }
              }
            },
            "safe_title": {
              "type": "string"
            },
            "extra_info": {
              "type": "object",
              "properties": {
                "is_cover": {
                  "type": "boolean"
                },
                "is_remix": {
                  "type": "boolean"
                },
                "acoustic": {
                  "type": "boolean"
                },
                "context": {
                  "type": "array"
                }
              }
            }
          }
        },
        "metadata": {
          "type": "object",
          "properties": {
            "query_words": {
              "type": "array"
            },
            "platform": {
              "type": "string"
            }
          }
        },
        "share": {
          "type": "object",
          "properties": {
            "intent": {
              "type": "string"
            },
            "expires": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "PERMALINK_UNKNOWN": "This permalink does not belong to any known platform"
    }
  ]
}

Lookup a permalink
GET/lookup{?q,mode}

Lookup a permalink to retrieve the info about the track or album

Capabilities

To be able to lookup on a platform, the platform must have the lookup capability.

Mode

See search

URI Parameters
HideShow
q
string (required) Example: https://open.spotify.com/track/6IWxHgVbdKyUMydhVGXRaT
mode
string (optional) Example: lazy

Aggregate

GET https://data.tuneefy.com/v2/aggregate/track?q=karma+police&mode=lazy&aggressive=false&include=deezer,spotify,qobuz
RequestsExampleWith errors
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "results": [
    {
      "musical_entity": {
        "type": "track",
        "title": "Karma Police",
        "album": {
          "title": "OK Computer",
          "artist": "Radiohead",
          "picture": "https://api.deezer.com/album/14879699/image",
          "safe_title": "OK Computer",
          "extra_info": {},
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": [
            "Deluxe Edition"
          ]
        },
        "links": {
          "deezer": [
            "http://www.deezer.com/track/138539981"
          ]
        },
        "safe_title": "Karma Police",
        "extra_info": {
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": []
        }
      },
      "metadata": {
        "score": 0.97,
        "merges": 1
      },
      "share": {
        "intent": "590c6c8320b1b"
      }
    }
  ]
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "results": {
      "type": "array"
    }
  }
}
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "FETCH_PROBLEM": "There was a problem while fetching data from the platform"
    }
  ],
  "results": [
    {
      "musical_entity": {
        "type": "track",
        "title": "Karma Police",
        "album": {
          "title": "OK Computer",
          "artist": "Radiohead",
          "picture": "https://api.deezer.com/album/14879699/image",
          "safe_title": "OK Computer",
          "extra_info": {},
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": [
            "Deluxe Edition"
          ]
        },
        "links": {
          "deezer": [
            "http://www.deezer.com/track/138539981"
          ]
        },
        "safe_title": "Karma Police",
        "extra_info": {
          "is_cover": false,
          "is_remix": false,
          "acoustic": false,
          "context": []
        }
      },
      "metadata": {
        "score": 0.97,
        "merges": 1
      },
      "share": {
        "intent": "590c6c8320b1b"
      }
    }
  ]
}
Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "errors": {
      "type": "array"
    },
    "results": {
      "type": "array"
    }
  }
}

Search on multiple platforms at once
GET/aggregate/{type}{?q,mode,aggressive,include}

Search for tracks or albums on multiple platforms

Mode

See search

Include

For aggregation, you can choose which platforms to aggregate to improve the response time of the endpoint. The parameter accepts a string which is a concatenation of the platforms tags separated by a comma.

GET aggregate/track?q=test&include=deezer,spotify,qobuz

If an unknown platform tag is passed, it is ignored. If the include parameter is empty or not given, the API will agreggate the results of all available platforms that are capable of searching this musical entity type.

Merging aggressively (tracks only)

The aggressive parameter allows to merge tracks without taking the album name into account. This works for a majority of scenarios since it’s quite rare that an artist released two tracks with exactly the same name, but it can sometimes confuse live or edit versions, for instance.

Acoustic versions, covers or remix should be correctly differentiated even if you merge aggressively.

URI Parameters
HideShow
type
string (required) Example: track
q
string (required) Example: karma+police
mode
string (optional) Example: lazy
aggressive
boolean (optional) Example: false
include
string (optional) Example: deezer,spotify,qobuz

Share

Share

GET https://data.tuneefy.com/v2/share/5911b14861018
RequestsExampleExpired or bad intent
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "uid": "k0bun0",
  "link": "http://tuneefy.com/s/track/k0bun0"
}
Headers
Accept: "application/json"
Authorization: "Bearer 5262d64b892e8d4341000001"
Responses200
Headers
Content-Type: application/json
Body
{
  "errors": [
    {
      "NO_OR_EXPIRED_INTENT": "No intent with the requested uid"
    }
  ]
}

Share an intent
GET/share/{intent}

Share a previous result as a tuneefy url, using the provided intent.

URI Parameters
HideShow
intent
string (required) Example: 5911b14861018

Postman configuration

Below is a Postman configuration if you want to test the API and fiddle with the parameters:

The Collection (V2) : tuneefy_API.postman_collection.json

The Environment : tuneefy.postman_environment.json

NB : In the collection, you must replace the key and secret with the ones provided for you, and the example intent must be replaced with a real intent from one of your calls to work properly.

API client examples

Here are very simple (and not optimized) examples to get you started. You can find these examples on github in the examples folder.

PHP

We use curl to retrieve the token and then perform a basic search.

<?php

$key = 'administrator';
$secret = 'password';

$host = 'https://data.tuneefy.com/v2';
$tokenEndpoint = $host.'/auth/token';
$searchEndpoint = $host.'/search/track/spotify?q=amon+tobin&limit=1';

// 1. Request token
$tk = curl_init($tokenEndpoint);
curl_setopt($tk, CURLOPT_HTTPHEADER, [
    'Content-Type: application/x-www-form-urlencoded;'.
    'charset=UTF-8'
]);
curl_setopt($tk, CURLOPT_POSTFIELDS, 
    'grant_type=client_credentials'.
    '&client_id='.$key.
    '&client_secret='.$secret
);
curl_setopt($tk, CURLOPT_RETURNTRANSFER, true);
$token = json_decode(curl_exec($tk));
curl_close($tk);

// 2. Use token for search on Spotify
if (isset($token->token_type) && $token->token_type === 'Bearer') {
    $br = curl_init($searchEndpoint);
    curl_setopt($br, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer '.$token->access_token,
        'Accept: application/json',
    ]);
    curl_setopt($br, CURLOPT_RETURNTRANSFER, true);
    $data = curl_exec($br);
    curl_close($br);
  
    // 3. Tada ! 
    echo "🎉\n";
    var_dump($data);
} else {
    echo "Wrong key/secret pair";
}

Shell

We use curl and jq too in a very simple fashion.

#!/bin/bash

response=$(curl -X POST --silent -d client_id=administrator -d client_secret=password -d grant_type=client_credentials https://data.tuneefy.com/v2/'auth/token)

token=$(echo $response | jq --raw-output '.access_token')

echo 🎉
curl --header "Authorization: Bearer $token" "https://data.tuneefy.com/v2/'search/track/spotify?q=amon+tobin&limit=1"

Javascript (node)

We use the request and json modules.

var request = require('request');
 
var key = 'administrator';
var secret = 'password';

var tokenEndpoint = 'https://data.tuneefy.com/v2/'auth/token';
var searchEndpoint = 'https://data.tuneefy.com/v2/'search/track/spotify?q=amon+tobin&limit=1';

// 1. Request token
request.post({
  url: tokenEndpoint,
  form: {
    'grant_type': 'client_credentials',
    'client_id': key,
    'client_secret': secret
  }
}, function(err, httpResponse, body) {
  var json = JSON.parse(body);

  if (json.token_type && json.token_type === 'Bearer') {
      // 2. Use token for search on Spotify 
      request({
        url: searchEndpoint,
        'auth': {
          'bearer': json.access_token
        },
        method: 'GET'
      }, function(err, httpResponse, body) {
        var json = JSON.parse(body);

        // 3. Tada ! 
        console.log("🎉");
        console.log(JSON.stringify(json, null, 4));
      });
  }

});

Python

We use the requests module (standard).

# coding=utf8
import requests, json

key = 'administrator'
secret = 'password'

tokenEndpoint = 'https://data.tuneefy.com/v2/'auth/token'
searchEndpoint = 'https://data.tuneefy.com/v2/'search/track/spotify?q=amon+tobin&limit=1'

# 1. Request token
payload = {'grant_type': 'client_credentials', 'client_id': key, 'client_secret': secret}
req = requests.post(tokenEndpoint,data=payload)

token = req.json()

# 2. Use token for search on Spotify
if token['token_type'] and token['token_type'] == 'Bearer':
    headers = {'Authorization': 'Bearer '+token['access_token'], 'Accept': 'application/json'}
    req = requests.get(searchEndpoint, headers=headers)

    # 3. Tada ! 
    print "🎉"
    print json.dumps(req.json(), indent=4, separators=(',', ': '))
else:
    print "Wrong key/secret pair"

Generated by aglio on 22 Oct 2018