design and implementation of a web service api with attachments

74
Design and Implementation of a Web Service API HANNES TYDÉN Master of Science Thesis Stockholm, Sweden 2011

Upload: others

Post on 03-Feb-2022

3 views

Category:

Documents


0 download

TRANSCRIPT

Design and Implementation of a Web Service API

H A N N E S T Y D É N

Master of Science Thesis Stockholm, Sweden 2011

Design and Implementation of a Web Service API

H A N N E S T Y D É N

Master’s Thesis in Computer Science (30 ECTS credits) at the School of Computer Science and Engineering Royal Institute of Technology year 2011 Supervisor at CSC was Serafim Dahl Examiner was Stefan Arnborg TRITA-CSC-E 2011:047 ISRN-KTH/CSC/E--11/047--SE ISSN-1653-5715 Royal Institute of Technology School of Computer Science and Communication KTH CSC SE-100 44 Stockholm, Sweden URL: www.kth.se/csc

AbstractWeb Service API:s are programming interfaces accessed through HTTP. API

designers have a high degree of freedom when defining these interfaces and

constraints must be set by the conventions of an architecture. REST is an

architectural style created to guide designers towards a uniform way of

defining such interfaces. ROA is an application architecture adhering to the

constraints of REST.

This report describes the design process of a Web Service API intended to

be used by third party client applications. Given a list of functional

requirements, the benefits of REST and ROA are discussed.

Sammanfattning

Design och implementering av ett webbtjänstapplikationsgränssnittWebbservice API-er är programmeringsgränssnitt exponerade över HTTP.

API designers har mycket frihet vid formgivning av dessa gränssnitt och

begränsningar måste sättas genom arkitekturkonventioner. REST är en

arkitektonisk stil skapad för att guida designers mot ett allmänt sätt att

definiera sådana gränssnitt. ROA är en applikationsarkitektur som bejakar

RESTs begränsningar.

Denna rapport beskriver designprocessen för ett webbservice API menat att

användas av tredjepartsklientapplikationer. Fördelar med REST och ROA

diskuteras givet en lista av funktionella krav.

PrefaceThis project was suggested and sponsored by SoundCloud Ltd. The report

describes the working process of the project and concludes my computer

science studies at the Royal Institute of Technology in Stockholm.

I wish to thank Alexander Ljung and Eric Wahlforss for founding

SoundCloud, Sean Treadway for useful input and Aline le Claire for all the

personal support.

I dedicate this work to my father.

Hannes Tydén

Berlin, April 2009

Table of ContentsProject Goals! 1

SoundCloud! 1

Strategic! 1

Technical! 1

SoundCloud API! 2

RESTfulness! 2

Scope of this Report! 2

Background! 4

Web Service API! 4

Hypertext! 4

Hypertext Transfer Protocol! 4

Representational State Transfer! 7

Resource Oriented Architecture! 7

Out of Band Information! 7

Content Types! 8

Ruby on Rails! 8

Request Routing! 8

Model – View – Controller Architecture! 9

Software Development Process! 9

SoundCloud Specific Terms and Concepts! 9

Requirements! 11

Functional Requirements! 11

Authentication and authorization! 13

Design! 14

Patterns! 14

Resource identifier! 15

Resources! 15

Resource Alias! 16

Interfaces! 17

Protected Resources and Methods! 18

Representations! 18

Roles! 18

Error Messages! 19

Implementation! 20

Resource Identifier Resolution! 20

Controllers! 21

Shared Behavior! 22

Representations! 22

Authentication! 22

Discussion! 24

RESTlessness! 24

Ruby on Rails Resources! 25

RPC — an Alternative Architecture! 25

Design! 25

Implementation! 27

Conclusions! 29

REST and ROA! 29

Design! 29

Implementation! 29

Documentation! 29

Meeting Future Requirements! 29

References! 31

Attachments! 32

API Documentation! 32

Project Goals

SoundCloudSoundCloud is a web application that enables people to send, receive and

distribute audio as easy as possible over the Internet. It serves as an online

storage for audio master files, which after initial upload can be shared and

distributed in formats that suit the requirements and needs of wide variety of

consumers. The master file can be annotated with metadata making it easier

to manage a catalog of works. Comments can be added to enable a focused

conversation about each piece. The audio files can be shared privately to

selected groups of people before they are released publicly. After being

made available publicly they can be accessed by anyone on soundcloud.com

or through embeddable widgets on blogs, websites or profile pages on social

networks.

StrategicThe traditional perception of the web is that it is a collection of

interconnected documents published by people intended to be read by other

people. This is changing since an increasing share of these documents (and

the connections between them) are being created and consumed by agent

applications. Making it easy for these applications to access a web

application makes it easy for third party developers to build services upon,

or integrating their existing services with, the SoundCloud API and as an

effect of this spread the reach of SoundCloud.

The exposure of the API implies that it needs to conform to common

practices to ensure ease of implementation of third party applications which

in turn should raise the level of adoption.

TechnicalA trend in web development is the increased use of asynchronous sending

and receiving of data in the web browser, circumventing the web browser’s

1

document loading centric data retrieval mechanism. This enables a richer

and more responsive user interface and might reduce server load.

This is employed by SoundCloud. Since the web server actually serves the

client application code to the user agent it has control over both server and

client behavior and responsibilities. As such, this point of integration could

be highly specialized and designed to which ever need SoundCloud would

find itself requiring. This leads to high complexity with different solutions

being used in each instance of front end and back end integration. Designing

an API for asynchronous requests with a uniform interface for the internal

communication will make it more predictable and easier to maintain.

SoundCloud APIThe SoundCloud API will be designed for external access by third party

applications as well as for internal access by the web application itself.

RESTfulnessAt the inception of this project the goal was to build a “RESTful” interface,

meaning it should not break any of the constraints of the Representational

State Transfer architectural style, described in Roy T. Fielding's dissertation

[1]. Many of the restrictions of this style are interesting and seem to be of

great benefit for creating an effective and efficient API. Working with this

project, however, proved that to fully adhere to all the restrictions would

lead to an increase of the implementation effort of the service with no

obvious benefit for client implementations. In this report the decisions on

whether to conform to or deviate from these restrictions, and the implication

of these decisions, will be described.

Scope of this ReportThis document only covers the aspects of the application exposed in the API

and the design, implementation and documentation thereof. The API is built

on top of the application’s domain model logic but even if this is crucial to

2

the functioning of the API, it will be considered a “black box” and will not

be discussed here.

3

BackgroundThis section explains concepts used throughout the report.

Web Service APIThe common view of an API is that it is a collection of functions for a

specific problem domain. The API exposes its functions over language

internal message passing or a foreign function interface protocol. A web

service API uses HTTP as the message passing standard.

In essence, all web applications, even though initially only designed for use

by humans with a web browser as the user agent, can be considered being

web services without having designated resources for this use. The problem

is only how well they are suitable for this use. Since they are not designed to

be invoked from other computer programs, they are most probably

cumbersome to use for this purpose.

HypertextHypertext is text annotated with links connecting it to different text

fragments creating a connected graph of text fragments.

HTML and XHTML are the most widespread hypertext languages. Anchors,

Links and Form elements in HTML-documents reference other resources on

the internet, making the web connected. [2] [3]

Hypertext is not used to connect resources within the SoundCloud API.

Hypertext Transfer ProtocolWeb Service clients communicate with the server using HTTP. The client

opens a connection to the server and sends a request to the server and

receives a response. Since HTTP is a stateless protocol the connection can

then be closed even if further requests are to be made from the same client

to the same server [3].

This document assumes usage of HTTP/1.1.

4

RequestA HTTP request consists of four parts: a resource identifier, a method, a set

of headers and potentially an entity body.

The identified resource is the object to be operated on. The method

describes what should be done to the resource. The headers are a set of

conditions that set constraints for what operations are to be performed and

how the response should be delivered. The entity body is data sent from the

client to be processed by the server.

MethodsThere are eight methods from which the following six are interesting to us:

GET, HEAD, POST, PUT, DELETE and OPTIONS. They are here

described as interpreted by REST.

Method Description

POST Creates a subordinate resource.

GET Retrieves a representation of the resource.

HEAD Retrieves only the headers and not the entity body of the resource.

PUT Updates the resource. If the URI of the resource is know prior to creation it can be created with a PUT request.

DELETE Destroys the resource.

OPTIONS Lists the methods available for the resource with the current authentication.

Table 1: HTTP methods

ResponseThe response to a request is a triplet containing a status code, a set of

headers and an optional entity body.

The status code identifies if the request was successful or erroneous and the

HTTP standard defines requirements for what information should be given

to the client with each code.

The headers contain metadata of the response, for instance which format the

response entity body is served in.

5

ResourceA resource is the central object on which the operations are performed. The

resource in itself is an abstract concept and it is exposed through different

representations. All data manipulation is done through these representations.

[1] [5]

The SoundCloud API resources are all abstractions of the SoundCloud

domain models.

Universal Resource IdentifierAll resources need to be uniquely identifiable or addressable. A URI can

only identify one resource, but this resource can be identified by name

different URI:s. [5]

The SoundCloud API has a strict URI scheme that enforces the

addressability of all exposed resources.

RepresentationThe representation is a serialization of a resource to a format decided by the

server or negotiated by the client. [1]

The SoundCloud API serves XML and JSON representations of its

resources.

Cookies and SessionsSince HTTP is a stateless protocol, data to identify a returning client must

be sent with each request. The server can ask the client to store data between

requests by sending a specific header. The client then sends this data with

each subsequent request, making it possible for the server to identify them.

Storing a unique variable on the client makes it possible for the server to

emulate a persisted session, which renders the connection stateful.

The SoundCloud web application uses this technique to keep a user logged

in over a span of requests and for the asynchronous requests made during

the viewing of a loaded page. [6]

6

Representational State TransferRepresentational State Transfer, REST, is an architectural style. It describes

how to design a distributed network architecture.

"REST is defined by four interface constraints: identification of resources;

manipulation of resources through representations; self-descriptive

messages; and, hypermedia as the engine of application state." [1]

These concepts will be revisited in the “Discussion” and “Conclusion”

sections.

Resource Oriented ArchitectureSince REST is not an architecture, Richardson and Ruby suggests a concrete

adoption of REST for HTTP based applications [7].

The four interface constraints declared for REST are translated into four

simpler properties of an application.

REST ROA

“identification of resources” Addressablility

“self-descriptive messages” Statelessness

“hypermedia as the engine of application state” Connectedness

“manipulation of resources through representations” A uniform interface

Table 2: Translation of RESTʼs constraints to ROAʼs properties.

The constraint of “self-descriptive messages” is not fully satisfied by the

“Statelessness” property of ROA. This constraint also implies that no “out

of band information” may be essential for the successful interpretation of a

resource’s representation.

Out of Band InformationInformation outside of a communication channel needed to understand the

message being transmitted is called “out of band information”. An exception

is when a message refers to a commonly known and adopted standard. [8]

7

Content TypesAs mentioned above, representations of resources can be sent and received

in different formats. Serving several formats minimizes the restrictions by

client parsing libraries. [9]

x-www-form-urlencodedThis is the way web browsers send form data. It is already implemented in

the Ruby on Rails framework. The entity body consists of key-value pairs

with escaped values, with framework specific rules for serialization of more

complex data structures. [10]

Extensible Markup LanguageXML is very capable of representing complex data structures. An XML

document is a tree structure, where each node can be annotated by attribute

name-value pairs. It is considered verbose and requires complex and

processor intensive parsing. [11]

JavaScript Object NotationJSON can represent complex data structure but lacks the possibility to

annotate the tree nodes. It is a lightweight data format and the serialization

is JavaScript source code, which means it can be de-serialized by any

JavaScript engine. [12]

Ruby on RailsRuby on Rails is a web application framework. It serves as a platform for

web applications and contains methods for generic functionality. [13]

It is implemented in the object oriented language Ruby. The Ruby language

is very well fitted for metaprogramming and can be made very compact and

expressive. [14]

Request RoutingThe application server receives a request with an HTTP method and URI,

and uses a list of rules to dispatch to a controller that builds the response.

8

Model – View – Controller ArchitectureThe controller receives the request and loads and modifies the data as

requested by the client. It then renders the response using a view negotiated

by the client. The result of the rendering is the representation being sent to

the client. [15]

Software Development ProcessSoundCloud employs a “agile-like” iterative software development process

working with short development cycles, about two weeks long, and then

releasing the application to beta testers which continually use the

application. Features are added in each cycle according to planning or from

user feedback.

This process can be translated as a repeating waterfall process where

requirements, design, implementation and testing are done over and over

again.

SoundCloud Specific Terms and ConceptsThese terms are not unique to SoundCloud but they have specific

implications in the context of this report.

UserA persistent user account, an agent for one or many people.

Current UserAuthentication is always done on the behalf of a single user. This user is

referred to as the “current user”.

TrackA track is the domain model that encapsulates an audio file and its metadata.

Public and Private SharingTracks are created and owned by a user and can be shared either publicly or

privately. When shared publicly, anyone can access the track’s metadata and

audio file. When shared privately, the owner gives access to it to a set of

9

SoundCloud users. These users will need to be authenticated to access the

track.

The owner of a track can grant, “share”, or revoke, “unshare”, user access to

a track at any time.

FollowingAffiliations can be made between users. These are unidirectional and control

the flow of information between the users.

CollectionSome resources respond with a collection of objects, resources in their own

right. These collections might be very large and are therefore partitionable

using offset and limit parameters bound under a specific ordering of the

objects.

DeploymentsThe SoundCloud application is deployed to two different hosts with their

own respective databases for persistence. One is the actual application and

is called “Live”. The other is a testing ground for application developers

where they can create and destroy resources without having to worry about

these changes being visible on the live site. This is called “Sandbox”.

10

RequirementsAs mentioned in the section “Software Development Process” above, the

requirements for the application have changed during the development of

the API. The requirements listed below are those that were valid when

writing this report.

Functional RequirementsThis list of requirements has been compiled from use cases of third party

clients and to support features for the web application. It is grouped by

relation to the domain models of the application.

They are also marked if they are need to be exposed in the internal and/or

external API:s. Access Control (AC) denotes whether the functionality

needs an identified actor. The legend is used as argument for the design of

the resources.

Requirement Int Ext AC Legend

Users

Show the current user x Owner

List users x Public U.01

Show a user x Public U.02

Update a user x Owner U.03

List tracks made by a user x Protected U.04

List tracks “favorited” by a user x Protected U.05

Add a track to a userʼs list of “favorites” x x Owner U.06

Remove tracks from a userʼs list of “favorites” x x Owner U.07

List playlists by a user x Protected U.08

List comments made by a user x Protected U.09

List other users that are “followed” by a user x Public U.10

Start “following” other users x x Owner U.11

Stop “following” other users x x Owner U.12

List other users that “follow” a user x Public U.13

11

Requirement Int Ext AC Legend

Tracks

List tracks x Protected B.02

Create a track x Owner E.07

Show a track x Protected E.03

Updating track meta data x Owner E.08

Remove a track x x Owner B.11

List users a track has been shared to x Protected B.21

“Share” a track to a user x Owner E.09

“Unshare” a track from a user x Owner E.10

List comments made on a track x Protected B.04

Create a comment on a track x x Protected B.12

Comments

Show a comment x Protected E.04

Remove a comment x x Owner B.13

Playlists

List playlists x Protected B.06

Create a playlist x x Owner B.18

Show a playlist x Protected E.05

Append a track to a playlist x x Owner B.19

Remove a track from a playlist x x Owner B.20

Events

List events x Owner B.07

Show a single event x Owner E.06

Remove an event x Owner I.01

Contact groups

List contact groups x Owner I.02

Create a contact group x Owner I.03

Update the name of a contact group x Owner I.04

Remove a contact group x Owner I.05

12

Requirement Int Ext AC Legend

Collections

Order a collection x x N/A C.01

Filter a collection with free text search x x N/A C.02

Partition a collection with offset and limit x x N/A C.03

Filter a collection on attribute value x x N/A C.04

Filter a collection on interval of attribute value x N/A C.05

Table 3: List of functional requirements.

Access control:

• Public – Anyone can retrieve this resource• Protected – if the current user is unauthenticated or unauthorized the

server will give no or partial information, or disallow the action.• Owner – The action is only allowed to a owner of the resource, this

property is derived from properties of the back-end model.

Authentication and authorizationSome of the functionality specified above behaves differently dependent on

whether the current user is authenticated or not. If a track is privately

shared, all its dependent resources are also private and can only be seen by

an authenticated and authorized user.

All state modifying operations require authentication, some also require

authorization.

13

DesignDesigning the API was done by going through the list of requirements and

translating them into resources.

From the list of functional requirements it is easy to find “primary” and

“secondary” resources. The secondary resources are only interesting and in

the context of a primary resource. In the list of resource identifiers this

hierarchy is explicit.

PatternsTo make the API as coherent as possible these patterns were identified.

A distinction is made between single resources and collections of resources.

Collections: Retrieve, Create subordinate resource.

Connecting resources: Create and Destroy

Primary ResourcesThese resources need to expose Create, Retrieve, Update and Destroy

1. Users

2. Tracks

3. Playlists

4. Comments

5. Groups

6. Events

Secondary resources“Connecting” resources do not contain any data except for creation date.

1. Followers: users and users

2. Favorites: users and tracks

3. Sharings: tracks and users

4. Listings: playlists and tracks (also with ordinal number)

14

5. Groups: users and groups

6. Members: groups and users

Resource identifierAll resources are backed with one or several models persisted in the

database. All domain model instances have an id which is unique for their

respective class. This id can be used to uniquely identify the model in the

scope of its class. Users, Tracks and Playlist also have a “permalink”

property which also is unique with respect to their classes. This provides a

more readable URI for the resource.

ResourcesThis list is the result of a translation of the functional requirements to

resources. With each resource identifier the CRUD (Create, Retrieve,

Update and Destroy) operations are exposed, a list of methods are given,

and with each method a reference to the functional requirements.

Resource Identifier Ops POST GET PUT DELETE

/users R B.01

/users/{uid} RU E.01 E.02

/users/{uid}/tracks R B.05

/users/{uid}/favorites CR B.16 B.08

/users/{uid}/favorites/{tid} CD B.16 B.17

/users/{uid}/playlists R B.22

/users/{uid}/comments R B.03

/users/{uid}/followings CR B.14 B.09

/users/{uid}/followings/{ouid} CD B.14 B.15

/users/{uid}/followers R B.10

/tracks CR E.07 B.02

/tracks/{tid} RUD E.03 E.08 B.11

/tracks/{tid}/shared_to CR E.09 B.21

15

Resource Identifier Ops POST GET PUT DELETE

/tracks/{tid}/shared_to/{uid} CD E.09 E.10

/tracks/{tid}/comments CR B.12 B.04

/comments/{cid} RD E.04 B.13

/playlists CR B.18 B.06

/playlists/{pid} RUD E.05

/playlists/{pid}/tracks CR B.19

/playlists/{pid}/tracks/{tid} CD B.19 B.20

/events R B.07

/events/{eid} RD E.06 I.01

/groups CR I.03 I.02

/groups/{gid} UD I.04 I.05

/groups/{gid}/members CR I.07 I.06

/groups/{gid}/members/{uid} CD I.04 I.08

Table 4: Definition of resources and operations

Abbreviation

uid User id/permalink

ouid User id/permalink of another user

tid Track id/permalink

cid Comment id

pid Playlist id

eid Event id

gid Group id

Table 5: Legend for resource URI templates

Resource AliasSince most applications are intended to act on the behalf of a user in order

to access protected resources, it was considered convenient to have a

resources alias for the currently authenticated user.

/me expands to /users/{cid} where cid is the currently authenticated user’s

id.

16

Alias Canonical URI

/me /users/{cid}

/me/tracks /users/{cid}/tracks

/me/favorites /users/{cid}/favorites

/me/favorites/{tid} /users/{cid}/favorites/{tid}

/me/playlists /users/{cid}/playlists

/me/comments /users/{cid}/comments

/me/followings /users/{cid}/followings

/me/followings/{ouid} /users/{cid}/followings/{ouid}

/me/followers /users/{cid}/followers

Table 6: Resource aliases

InterfacesInterfaces are the behaviors found to be repeating in the different collection

resources. They define a set of parameters or parameter naming convention.

In the documentation it is specified which interfaces are supported by the

different collection resources.

Interface Parameters

Order order={attribute}%20{ASC|DESC}

Order the collection by the given attribute.Order the collection by the given attribute.

Search q={query}

Filters a subset of the collection based on free text search in the members.Filters a subset of the collection based on free text search in the members.

Partitioning offset={number}, limit={number}

Filters a subset of the collection based on position in the collection.Filters a subset of the collection based on position in the collection.

Attribute filtering filter={attribute name}

Filters a subset of the collection based on boolean values Filters a subset of the collection based on boolean values

Attribute value interval {attribute}[from]={lower_bound},{attribute}[to]={upper_bound}

Filters a subset of the collection based on inclusion of a value in a value rangeFilters a subset of the collection based on inclusion of a value in a value range

Table 7: Definition of collection resource scoping parameters.

17

Protected Resources and MethodsResources that are not available to the public are considered protected.

Some methods on specific resources are protected. These are all listed in the

functional requirements.

RepresentationsThe resource representations can simply be seen as sets of key-value pairs.

Example 1: The “id” and “uri” attributes are included in all representations.

In some cases the representations in their own right is nestled in a another

resource representation for convenience. The representation for Tracks is an

example where the owning User is nestled.

Example 2: The User representation is nestled in the Track representation.

The representations are fully described in the documentation.

RolesThe representations of resources are reused. The representation of the

“followers” and “sharings” resources has the exact same structure as the

“users” resource.

{ id: {id}, uri: http://api.soundcloud.com/{collection-name}/{id}, /* ... */}

{ id: [track-id], uri: http://api.soundcloud.com/tracks/[track-id], title: [track-title] user: { id: [user-id], uri: http://api.soundcloud.com/users/[user-id],, username: [user-username], /* ... */ } /* ... */}

18

Error MessagesAs mentioned above, the HTTP specification defines a set of status codes to

classify a response. The 400 and 500 status code ranges are used to

communicate errors to the clients. For certain codes it is required that an

entity body describing the error is sent; for some codes an entity body is

sent for client convenience.

For simplicity, the entity body will always be a list of one or more error

messages.

Code Description Message

400 Bad Request * A list of model validation errors or parameter errors.

401 Unauthorized * Description on how to authenticate.

403 Forbidden Reason for denying the request.

406 Not Acceptable A list of supported response formats.

409 Conflict A description of the conflict.

415 Unsupported Media Type * “Could not parse entity body. {format} expected.” or “{format} not supported.”

500 Internal Server Error * “An unexpected error occured.”

Table 8: List of response codes and entity body content for error messages.

The 505 “HTTP Version Not Supported” requires a response entity body but

this should be handled by the web server.

Example 3: Entity body of an erroneous request.

{ errors: [ “Track title must be at least 3 characters long.”, “Track must have a audio file uploaded.” ]}

19

Implementation

Resource Identifier ResolutionResolution of URIs in Rails is called “routing” and has been described

above. The syntax is very compact. Below is an example of how to define

three sets resources, two primary sets, “users” and “tracks”, and one

secondary set, “users/{uid}/tracks”. The resulting resources are also listed

below.

Example 4: Routing users and tracks.

Method URI Controller Action

UsersUsersUsersUsers

GET /users users index

POST /users users create

GET /users/{uid} users show

PUT /users/{uid} users update

DELETE /users/{uid} users destroy

GET /users/{uid}/tracks tracks index

POST /users/{uid}/tracks tracks create

TracksTracksTracksTracks

GET /tracks tracks index

POST /tracks tracks create

GET /tracks/{tid} tracks show

PUT /tracks/{tid} tracks update

DELETE /tracks/{tid} tracks destroy

Table 9: Excerpt of resources and available methods generated from routing code in Example 4.

ActionController::Routing::Routes.draw do |map| map.resources :users do |user| user.resources :tracks end map.resources :tracksend

20

ControllersThe actions mentioned in Table 9 are methods of a controller class, and are

responsible for retrieving and modifying the resource. Example 5 shows

how a controller is structured.

Example 5: Controller with the necessary actions defined as methods.

module Api class TracksController < ApiController # Include methods and behaviour common to all # resources rendering tracks. include TracksResource # Authentication is required for all actions # except the index and show actions auth_policy :optional => [ :index, :show ] def index # Load a collection of Track models # within scope # Render their representations end def show # Load a Track model # Render its representation end def create # Create a Track model # within scope # If creation is successful # Render its representation # Else # Render list of error messages end def update # Load and update a Track model # If update is successful # Render its representation # Else # Render list of error messages end def destroy # Load and destroy a Track model end endend

21

Shared BehaviorIn example 5, we see that the controller inherits from the “ApiController”.

This is true for all controllers handling resources exposed in the API. The

ApiController defines behavior specific to serve the API request and is a

subclass of “ApplicationController”, which defines behavior to serve all

requests for both soundcloud.com and api.soundcloud.com.

“Roles” are mentioned above. They do not only share representations but

also pose requirements upon the behavior of the controller. This behavior is

defined in modules included in the different controllers with the same roles.

RepresentationsThe representations are implemented as a set of rules on how to serialize a

resource. Behaviors for responding in different formats are defined in the

superclass of all representations.

AuthenticationSoundCloud employs three different authentication mechanisms, each

meant to be used in a specific context.

OAuthOAuth is the primary mechanism for authentication against the live system.

The client requests an “access token” authorized by a user to act on its

behalf. This access token is used to sign requests to protected resources

[16].

Cookie BasedThe web application uses cookie based session emulation to keep track of

authenticated users [6].

HTTP Basic AuthSince OAuth is fairly complicated and requires special libraries that create

an opaque wrapper around the actual request, standard HTTP Basic Auth is

22

allowed to api.sandbox-soundcloud.com. This makes it easier for client to

see what happens when making requests to the API [17].

23

Discussion

RESTlessnessWhen the project started the idea was to implement a RESTful API. The

main reason for this was the ignorance concerning the constraints of the

REST architectural style. While working with the design of the API these

constraints became apparent, and questioned whether they should be

honored or not.

The API honors two of the four constraints, namely: “identification of

resources” and “manipulation of resources through representations”.

The two other constraints, “self-descriptive messages” and “hypermedia as

the engine of application state” (HEAS) are not honored; hence it cannot be

claimed that the API is RESTful.

Approaches to obtain RESTfulnessTo adhere to the HEAS-constraint the API needs to serve at least parts of its

representations in a hypertext format. These hypertext documents would

include links (a-tags with href-attributes) to related resources. To create new

resources a “new” resource, and to update existing resources an “edit”

resource would be needed. The representations of both resources would

contain HTML-forms to show what kind of data that can be posted to create

or update the resource. In this case XHTML using microformats would be

the best candidate. Another option would be to create custom hypertext

formats [3] [7] [18].

The constraint of “self-descriptive messages” would be solved by similar

techniques. To adhere to this constraint all links would need to be labeled

appropriately, which requires a hypertext format.

The current representations are fairly compact, simple to understand, parse

and manipulate. Forcing clients to parse XHTML would make them less

efficient and more error prone. The drawback is that the client developers

need to rely on out of bound information to understand what resources are

exposed and how to manipulate them.

24

Ruby on Rails ResourcesThere are many plugins for resource oriented services for Rails. The

drawback is that they are often tightly coupled to the backing models. This

means that any changes to the models would also change the publicly

exposed API.

Using these would not make the API more RESTful.

RPC — an Alternative ArchitectureRPC, and especially XML-RPC, are well defined architectures with wide

adoption and libraries for most languages and platforms. As mentioned in

the previous section, resource oriented services are well supported in Rails

and implementing a RPC service would be to go against the grain of the

framework and probably prove to be cumbersome.

DesignURI-schemeThe URI-scheme is very strict and well defined. Its hierarchical structure

clearly shows the relationships of the concerned resources. By

understanding the URI-scheme it is easy to navigate through the resources

of the application.

URI-spaceThe SoundCloud application and the web service do not share URI-space.

Having them sharing URI-space would be optimal from an interface

perspective, but it turns out that the behavior of the different resources is too

different to be controlled by the same mechanisms.

Also, the actual URIs cater to different needs. One is for legibility for

humans and the other is for consistency in a programming environment.

Using a combined URI-space would further complicate the distinction

between where to use the different authentication mechanisms. By serving

the API from a different host, these requests can be distinguished from

“normal” requests, and require OAuth authentication.

25

Another benefit of serving the API from a different host is the ability to

employ more complicated load balancing, where different amounts of

resources can be given to the web application and the service.

ResourcesOn step towards RESTfulness would be to expose “new” and “edit”

resources for each editable canonical resource. These resources would show

what attributes of a resource are writable.

Representations and RolesThe XML format of the resources contains type information because of the

nature of the XML-document structure. The JSON format does not contain

this information, which implies that the client already know what kind of

representation it handles. Because of the strict URI-scheme this is known,

but relies on out of band information. Therefore a “type” attribute should be

added to the representations.

The nestled representations of related resources are only partial

representations, which needs to be communicated to the client. A

“complete” attribute with a boolean value can be added to the

representations to indicate this. The canonical URI to the nestled resource is

always present in the partial representation and can be used to resolve the

complete representation.

The re-use of representations in different roles appeared smart and simple at

a first impression, but when considering the “connectedness” of the service

there are missing links. In some collections ordering information might also

not be missing. This can make the resources to seem to behave weirdly. To

alleviate this problem, new representations containing a partial

representation of the related resource need to be added for these roles.

Collection Resource InterfacesThe interfaces were added to the resources in an ad-hoc manner and not

very well thought through. There are obvious limitations to them. Below are

some suggestions for a more consistent and expressive set of parameter

constraints.

26

Interface Parameters

Order order=(+|-){attribute}

Order the collection by the given attribute.Order the collection by the given attribute.

Search q={query}

Filters a subset of the collection based on free text search in the members.Filters a subset of the collection based on free text search in the members.

Partitioning offset={number}, limit={number}

Filters a subset of the collection based on position in the collection.Filters a subset of the collection based on position in the collection.

Attribute filtering filter([{index}])([+|-])[{name}]={value}

Filters a subset of the collection based on values Filters a subset of the collection based on values

Attribute value interval {attribute}[from]={lower_bound}, {attribute}[to]={upper_bound}

Filters a subset of the collection based on inclusion of a value in a value rangeFilters a subset of the collection based on inclusion of a value in a value range

Table 10: Better interfaces for collection resources.

Internal useIn the beginning of this project, the API was intended to also be used for

internal asynchronous requests. For simple PUT and DELETE requests, as

long as no response entity body was needed, this approach was very easy to

use. But, as soon as HTML fragments needed to be rendered this became

tedious as we needed to cater to a lot of special cases, in turn making the

implementation more complex than needed.

To support the behavior for internal use minor features needed to be added.

These features were exposed in the external API, even if undocumented.

The “common controllers” need to build up state to send to the client for the

next request to the API controllers. This state has now been moved to the

back end, which simplifies both the server as the client implementations.

ImplementationIn comparison to the effort made to design the API, implementation has

been fairly trivial. The support for resource oriented applications is very

well developed in the Ruby on Rails framework.

27

The two hardest parts was to implement support for OAuth and the

consistent rendering of the representations.

OAuth was implemented by using a plugin library, but because of

immaturity of this library a lot of uncertainties arose, which were time

consuming to debug and reproduce.

Rendering the representations of the resources was executed using

“presenters”, which is the wrong term for describing their responsibility.

“Translators”, “decorators” or even “views” would be more descriptive

terms. A big mistake was to only implement these presenters for one way

use. A suggestion for improvements would be a two way translator from

input to a data type that can update or create a resource.

28

Conclusions

REST and ROAIn hindsight the goal to implement a RESTful web service seems almost

naïve. The constraints are far too strict to serve a purpose for an effective

API, even from a client perspective. The ambition should not be disdained,

as implementing just the uniform interface for all resource should be

considered a great benefit.

DesignWhen criticizing the design of the API, the knowledge of the requirements

must be taken into consideration. Both the underlying application as well as

the requirements from existing or potential client applications have been

changing rapidly during the course of this project. In the discussion section

of this report some suggestions are made on how to further improve the API

though many of these were realized after the faulting decisions had been

made publicly exposed.

ImplementationAs mentioned in the discussion section above, the implementation was the

least effort of realizing this project. The most important part of the API is its

consistency. Changing the supporting implementation is something that

client developers should never notice.

DocumentationBased on contact with developers, the best way to get people involved is

give simple examples of what is possible to do with the API.

Meeting Future RequirementsClient developers are recommended to anticipate change. If attributes are

added to representations or new resources are added clients should not

29

break, but when essential parts are removed, moved or renamed there is no

guarantee that clients can handle this change.

A possible solution would be to have different versions of the API on

different host in parallel, perhaps at most the two most recent versions. For

example, v1.api.soundcloud.com and v2.api.soundcloud.com, with

api.soundcloud.com as an alias for the latest version. Developers would be

given instructions how to alter their implementations to upgrade to a later

version.

This would of course increase the complexity of the application and the

effort to maintain two stable versions is tedious. A requirement for this

would be to build an extensive test suite for each version of the API to be

sure that the existing clients will not break.

30

References1. Fielding R. 2000 – Architectural Styles and the Design of Network-

based Software Architectures, University of California2. HTML 4.01 Specification – http://www.w3.org/TR/1999/REC-

html401-19991224 – 2009-04-103. XHTML 1.0 Specification – http://www.w3.org/TR/2002/REC-

xhtml1-20020801 – 2009-04-104. Hypertext Transfer Protocol 1.1 specification – http://www.w3.org/

Protocols/rfc2616/rfc2616.html – 2009-04-025. Uniform Resource Identifiers (URI): Generic Syntax RFC 2396 –

http://www.ietf.org/rfc/rfc2396.txt – 2009-04-106. HTTP State Management Mechanism – http://www.w3.org/Protocols/

rfc2965/rfc2965 – 2009-03-307. Ruby, S., Leonardson, R. – RESTful Web Services – ISBN-13:

978-05965292608. Out of Band, Wikipedia – http://en.wikipedia.org/wiki/Out-of-band –

2009-04-109. Mime Types RFC 2046 – http://tools.ietf.org/rfc/rfc2046.txt –

2009-04-1010. Uniform Resource Locators RFC 1738 – http://www.ietf.org/rfc/

rfc1738.txt – 2009-04-1011. XML Specification – http://www.w3.org/TR/REC-xml/ – 2009-03-3012. JSON Specification – http://www.ietf.org/rfc/rfc4627.txt – 2009-03-3013. Ruby on Rails Framework Documentation – http://api.rubyonrails.org/

– 2009-04-2814. Ruby Core Documentation – http://www.ruby-doc.org/core/ –

2009-04-2815. Fowler, M. – Patterns of Enterprise Application Architecture,

Addison-Wesley Professional – ISBN-13: 978-032112742616. OAuth Core Specification – http://oauth.net/core/1.0/ – 2009-03-3017. HTTP Authentication: Basic and Digest Access Authentication –

http://www.ietf.org/rfc/rfc2617.txt – 2009-04-2818. Microformats – http://microformats.org/about/ – 2009-04-28

31

Attachments

1. SoundCloud API Documentation

32

SoundCloud API Documentation

Table of Contents• Introduction

• Getting Started• Simple Use Case – Uploading an audio file

• Authentication• Basic HTTP• OAuth

• Resource Types• User• Track• Playlist• Comment• Event

• Comment• Track• Fan• Favorite

• Response Formats• Request Content Types• Cross Site Scripting

• JSONP• Collections

• Partitions• Client Forward Compatibility• Resources

• users• tracks

• Downloading tracks• Sorting by hotness

• playlists• comments• events

• DropBox

33

IntroductionPlease note that the API, as well as the web application, is still beta. This

means that things can change.

The API is built with the design principles of REST in mind, exposing

resources which can be manipulated using the HTTP methods GET, POST,

PUT and DELETE.

If you have questions, comments or suggestions they are very welcome.

Join the discussion on our mailing list or, if you wish to keep it private, send

an email to [email protected].

If you’ve already read this document from top to bottom, you might want to

check the list of changes to this document.

Getting StartedExamples throughout this document will mainly be illustrated using cURL.

We start by requesting the tracks that are publicly accessible:

Using cURL:

$ curl 'http://api.soundcloud.com/tracks'< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">11</id> <title>Final Countdown</title> ... <sharing>public</sharing> </track> <track> <id type="integer">21</id> <title>The Musical Offering</title> ... <sharing>public</sharing> </track></tracks>

To get all request and response headers do the cURL request in verbose

mode using the -v flag.

34

Simple Use Case – Uploading an audio fileWe’ve put up a simple use case how to upload a track and share it to other

users using curl from a shell.

AuthenticationAnyone can read Users, public Tracks and Comments on public Tracks.

Other resources are protected and require authentication.

Basic HTTPBasic HTTP is only available in our sandbox environment. The intention is

to give easy access to play around, without being able to manipulate

production data.

$ curl -u joey:hairspray 'http://api.sandbox-soundcloud.com/me'

OAuthTo access protected resources you can authenticate with OAuth, a standard

to give third party applications the authority to act on a user’s behalf. You

need to register a client application which is connected to your SoundCloud

user account. We’re working on extending this section of the

documentation. In the meantime you’ll find all the OAuth endpoints you

need after you register an application.

If you’re not already familiar with OAuth, please read more on oauth.net

where there is also a getting started series. Subscribing to the OAuth

mailing list will keep you updated.

If you want to give it a go you can look at our OAuth Example on how to

authorize a third party application.

35

Resource TypesThe resources can have different roles, but when processing the response

data from the API the five resource types are all you have to keep in mind.

UserUsers are the active object. Users are able to create Tracks, Comments,

make other Users their contacts and make Tracks favorites.

XML representation:

<user> <id type="integer">1</id> <username>joey</username> <full-name>Joey Tempest</full-name> <description>Stormwind, just like a wind.</description> <city>Upplands Väsby</city> <country>Sweden</country> <discogs-name>joeytempest</discogs-name> <myspace-name>joeyt</myspace-name> <website>http://www.europe.com/~joeyt/</website> <website-title>Home of the Stormwind</website-title> <online>true</online> <avatar-url>http://a1.soundcloud.com/profile_images/0000/0000/i123456.jpg</avatar-url> <permalink-url>http://soundcloud.com/joey</permalink-url> <uri>http://api.soundcloud.com/users/1</uri> <track-count>1</track-count></user>

36

TrackTracks have two different sharing modes, public and private. Public tracks

are accessible to all users and private tracks are only accessible to users to

whom the Track’s creator has explicitly given permission.

XML representation:

<track> <id type="integer">1</id> <user-id type="integer">881091440</user-id> <title>Final Countdown</title> <permalink>final-countdown</permalink> <description>This is a song I wrote with some friends a couple of years ago.</description> <sharing>public</sharing> <bpm type="float">118.2</bpm> <comments-count type="integer">23</comments-count> <created-at type="datetime">2008-02-20T18:44:35+01:00</created-at> <downloadable type="boolean">false</downloadable> <downloads-count type="integer">34</downloads-count> <duration type="integer">31000</duration> <genre>Rock</genre> <streamable type="boolean">true</streamable> <uri>http://api.soundcloud.com/tracks/1</uri> <user> <uri>http://api.soundcloud.com/users/1</uri> <username>joey</username> <permalink>joey</permalink> <permalink-url>http://soundcloud.com/joey</permalink-url> </user> <permalink-url>http://soundcloud.com/joey/final-countdown</permalink-url> <playback-count>644</playback-count> <artwork-url>http://a1.soundcloud.com/artworks/0200/120/i02911_big.jpg</artwork-url> <waveform-url>http://a1.soundcloud.com/assets/1/072/001/00001026/waveform-medium.png</waveform-url> <purchase-url>http://my-music-shop.com/tracks/final-countdown/</purchase-url> <stream-url>http://media.soundcloud.com/joey/final-countdown.mp3</stream-url> <user-playback-count>2</user-playback-count> <user-favorite>false</user-favorite></track>

37

Playlist (Set)Playlists are collections of tracks created by the owner of the tracks. Full

search, filtering and usage documentation here.

XML representation:

<playlist> <created-at type="datetime">2008-07-22T13:28:59+02:00</created-at> <description/> <genre/> <id type="integer">1</id> <permalink>my-own-set</permalink> <title>My Set</title> <user-id type="integer">1</user-id> <permalink-url>http://soundcloud.com/joey/sets/my-own-set</permalink-url> <uri>http://api.soundcloud.com/playlists/1</uri> <artwork-url> http://a1.soundcloud.com/images/default_artwork_big.png?5216 </artwork-url> <duration>538320</duration> <type/> <user> <uri>http://api.soundcloud.com/users/1</uri> <username>joey</username> <permalink>joey</permalink> <permalink-url>http://soundcloud.com/joey</permalink-url> </user> <tracks> <track> <created-at type="datetime">2008-06-25T16:38:02+02:00</created-at> ... <user-favorite>false</user-favorite> </track> <track> <created-at type="datetime">2007-07-28T18:58:54+02:00</created-at> ... <user-favorite>false</user-favorite> </track> </tracks></playlist>

38

CommentComments can be made on a Track. Users can only read comments made on

Tracks they have access to. If a timestamp is supplied the comment will be

displayed at, or around, that time during playback of the Track on the

SoundCloud website.

XML representation:

<comment> <id type="integer">1</id> <track-id type="integer">1</track-id> <user-id type="integer">1</user-id> <body>Great guitar solo!</body> <timestamp type="integer">2010000</timestamp> <uri>http://api.soundcloud.com/comments/1</uri></comment>

39

EventEvents are notifications informing a User about things that have happened

concerning them, and contains the related resources.

They are always ordered with the latest events first.

In the examples below the User receiving the event is called “the recipient”.

CommentAnother User creates a Comment on a Track of the recipient or in a

Comment thread where the recipient has also made a comment.

<event> <created-at type="datetime">2008-04-04T11:05:15+02:00</created-at> <id type="integer">4</id> <resource_id type="integer">1</id> <type>Comment</type> <comment> <id type="integer">1</id> ... </comment></event>

TrackA User gives the recipient access to a private Track or a User whom the

recipient is a fan of creates a public Track.

<event> <created-at type="datetime">2008-04-03T11:05:15+02:00</created-at> <id type="integer">3</id> <resource_id type="integer">1</id> <type>Track</type> <track> <id type="integer">1</id> ... </track></event>

40

FanA User adds the recipient as a contact, hence becomes a fan of the recipient.

<event> <created-at type="datetime">2008-04-02T11:05:15+02:00</created-at> <id type="integer">2</id> <resource_id type="integer">1</id> <type>Fan</type> <user> <id type="integer">1</id> ... </user></event>

FavoriteA User adds one of the recipient’s Tracks to their favorites.

<event> <created-at type="datetime">2008-04-01T11:05:15+02:00</created-at> <id type="integer">1</id> <resource_id type="integer">1</id> <type>Favorite</type> <track> <id type="integer">1</id> ... </track></event>

41

Response FormatsRepresentations in XML and JSON are available.

Choose response format by either appending .format to the URL:

http://api.soundcloud.com/me.xml

http://api.soundcloud.com/me.json

http://api.soundcloud.com/me.js

or send an Accept header specifying the mime type.

When using cURL:

$ curl 'http://api.soundcloud.com/me/' -H 'Accept: text/xml'

$ curl 'http://api.soundcloud.com/me/' -H 'Accept: application/xml'

$ curl 'http://api.soundcloud.com/me/' -H 'Accept: application/x-xml'

$ curl 'http://api.soundcloud.com/me/' -H 'Accept: application/json'

$ curl 'http://api.soundcloud.com/me/' -H 'Accept: text/javascript'

The default response format is XML.

Request Content TypesWhen creating or updating resources data can be passed as url encoded or in

XML-format. The two examples below are equivalent.

URL encoded$ curl 'http://api.soundcloud.com/tracks' -X POST -d \'track[title]=Superstitious&track[description]=A song about people.'

XML$ curl 'http://api.soundcloud.com/tracks' -X POST \-H 'Content-Type: application/xml' -d \'<track> <title>Superstitious</title> <description>A song about people.</description></track>'

42

The examples below will use XML-format for legibility.

Sending data as JSON is planned for the official release.

Cross Site Scripting

JSONPWhen you want to embed SoundCloud content in script-tags, request

JavaScript and pass the name of your response handler in the callback-

parameter.

Example:

<script src="http://api.soundcloud.com/me.js?callback=XSS.JSONP.callback" type="text/javascript"></script>

CollectionsIf a response contains more than one result it is served as a collection of

resources. Theses collections always contain only one type of resources.

PartitionsCollections are served in partitions limited to a maximum of 50 items.

When requesting a collection it is possible to pass two parameters: offset

and limit. offset defaults to 0 and limit defaults to 50, if a larger value for

limit is passed it is set to 50. If you receive fewer items than you asked for

you are at the end of the collection.

Retrieving the whole collection can be done by repeatedly requesting the

collection, starting with an offset value of 0, and then increasing the offset

for each request until the response contains fewer elements than requested

or if it is empty.

Client Forward CompatibilityThe API may change when new features are introduced. New attribute and

sub resources might be added to existing resource types and resource types

might be added.

43

Resources

usersResource Methods

/users GET

/users/{user-id} GET, PUT

/users/{user-id}/tracks GET

/users/{user-id}/comments GET

/users/{user-id}/contacts GET

/users/{user-id}/contacts/{contact-id} GET, PUT, DELETE

/users/{user-id}/favorites GET

/users/{user-id}/favorites/{favorite-id} GET, PUT, DELETE

For each resource /users/{user_id}* there is a resource /me*. Using these

resources gives the logged in user as the given user.

GET /usersType: Collection of Users

Retrieves all users.

$ curl 'http://api.soundcloud.com/users'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><users> <user> <id type="integer">1</id> <username>joey</username> ... </user></users>

44

The resource is searchable by passing the query to the resource. It then lists

a subset matching the search query.

$ curl 'http://api.soundcloud.com/users?q=seba'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><users> <user> <id type="integer">2</id> <username>jsb</username> <full-name>Johann Sebastian Bach</full-name> ... </user> <user> <id type="integer">5</id> <username>seba</username> <full-name>Sebastian Ahrenberg</full-name> ... </user></users>

GET /users/{user-id}GET /meType: User

The given user.

$ curl 'http://api.soundcloud.com/me'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><user> <id type="integer">1</id> <username>joey</username> ...</user>

45

GET /users/{user-id}/tracksGET /me/tracksType: Collection of Tracks

The given user’s uploaded tracks.

$ curl 'http://api.soundcloud.com/me/tracks'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">12</id> <title>Carrie</title> ... <sharing>private</sharing> </track> <track> <id type="integer">11</id> <title>Final Countdown</title> ... <sharing>public</sharing> </track></tracks>

Requesting the tracks resource of another user:

$ curl 'http://api.soundcloud.com/users/2/tracks'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">21</id> <title>The Musical Offering</title> ... <sharing>public</sharing> </track></tracks>

The resource is searchable by passing the query to the resource. It then lists

a subset matching the search query.

46

GET /users/{user-id}/commentsGET /me/commentsType: Collection of Comments

Comments made by the given user.

GET /users/{user-id}/contactsGET /me/contactsType: Collection of Users

The given user’s contacts.

$ curl 'http://api.soundcloud.com/users/2/contacts'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><users> <user> <id type="integer">3</id> <username>vice</username> ... </user> <user> <id type="integer">4</id> <username>mchammer</username> ... </user></users>

PUT /users/{user-id}/contacts/{contact-id}PUT /me/contacts/{contact-id}Adds the user with the id contact_id to the given user’s list of contacts.

Responds with 403 Forbidden if the given user is not the logged in user.

$ curl 'http://api.soundcloud.com/users/1/contacts/21' -X PUT

< HTTP/1.1 201 Created

47

DELETE /users/{user-id}/contacts/{contact-id}DELETE /me/contacts/{contact-id}Removes the user with the id contact-id from the given user’s list of

contacts. Responds with 403 Forbidden if the given user is not the logged in

user.

$ curl 'http://api.soundcloud.com/users/1/contacts/21' -X DELETE

< HTTP/1.1 200 OK

GET /users/{user-id}/fansGET /me/fansType: Collection of Users

The given user’s fans.

$ curl 'http://api.soundcloud.com/users/2/fans/'

< HTTP/1.1 200 OK

GET /users/{user-id}/favoritesGET /me/favoritesType: Collection of Tracks

The given user’s favorited tracks.

$ curl 'http://api.soundcloud.com/users/1/favorites/'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">21</id> <title>The Musical Offering</title> ... <sharing>public</sharing> </track></tracks>

48

PUT /users/{user-id}/favorites/{favorite-id}PUT /me/favorites/{favorite-id}Adds the given track to the given user’s list of favorites. Responds with 403

Forbidden if the given user is not the logged in user.

$ curl 'http://api.soundcloud.com/me/favorites/3' -X PUT

< HTTP/1.1 201 Created

DELETE /users/{user-id}/favorites/{favorite-id}DELETE /me/favorites/{favorite-id}Adds the given track to the given user’s list of favorites. Responds with 403

Forbidden if the given user is not the logged in user.

$ curl 'http://api.soundcloud.com/users/1/favorites/3' -X DELETE

< HTTP/1.1 200 OK

49

tracksResource Methods

/tracks GET

/tracks/{track-id} GET, PUT, DELETE

/tracks/{track-id}/comments GET, POST

/tracks/{track-id}/permissions GET, PUT

/tracks/{track-id}/download GET

GET /tracksType: Collection of Tracks

Collection of all tracks.

Parameter Name Value Description

filter all | public | private | streamable | downloadable

comma separated list

order created_at | hotness

bpm[from] number

bpm[to] number

created_at[from] date

created_at[to] date

ids numbers comma separated list

50

Unauthenticated request:

$ curl 'http://api.soundcloud.com/tracks'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">11</id> <title>Final Countdown</title> ... <sharing>public</sharing> </track> <track> <id type="integer">21</id> <title>The Musical Offering</title> ... <sharing>public</sharing> </track></tracks>

Authenticated request:

$ curl 'http://api.soundcloud.com/tracks'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <id type="integer">11</id> <title>Final Countdown</title> ... <sharing>public</sharing> </track> <track> <id type="integer">12</id> <title>Carrie</title> ... <sharing>private</sharing> </track> <track> <id type="integer">21</id> <title>The Musical Offering</title> ... <sharing>public</sharing> </track> <track> <id type="integer">22</id> <title>Ice, Ice, Baby</title> ... <sharing>private</sharing> </track></tracks>

51

The collection is searchable by passing the query to the resource. It then

lists a subset matching the search query.

$ curl 'http://api.soundcloud.com/tracks?q=ice'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><tracks type="array"> <track> <bpm type="float" nil="true"></bpm> <comments-count type="integer">0</comments-count> <created-at type="datetime">2008-02-26T17:39:24+01:00</created-at> ... <sharing>public</sharing> </track></tracks>

Downloading tracksTracks that are downloadable can be downloaded from the download-url in

the track representation.

<track> <id type="integer">11</id> <title>Final Countdown</title> ... <downloadable>true</downloadable> <download-url>http://sandbox-soundcloud.com/joey/final-countdown/download</download-url></track>

To download a public Track, do a request to the URL and follow the redirect

to get the file.

Downloading a private Track requires authentication, and there are some

restrictions that changes the way we get the file. Authentication is done with

OAuth and when requesting a http://sandbox-soundcloud.com/{user}/

{track}/download resource the OAuth parameters MUST be sent in the

querystring. Also note that the host is “sandbox-soundcloud.com” and not

“api.sandbox-soundcloud.com”. For the production server the host is

“soundcloud.com”.

So if “Final Countdown” is a private Track and we want to download it we

would get a signed URL looking something like:

52

http://sandbox-soundcloud.com/joey/final-countdown/download?oauth_nonce=CSZMby3XFbfjqZB9LwYPYsyZqEbgTiclekrTyfaOeM&oauth_signature_method=HMAC-SHA1&oauth_token=SdMzbtW5ZQNFmkiVZrWxLA&oauth_timestamp=1225906320&oauth_consumer_key=xqw4ef0C4iqS5PC5yaX4WA&oauth_version=1.0&oauth_signature=4pVMW43V4GOMBGPomjr9pO56BBo%3D

Sorting by hotnessThe hotness of a Track is based on activity around the Track, such as

comments on the Track, Users making Tracks their favorites and playbacks.

The optional date interval for the hotness ordering is the time period when

the tracks were “considered hot”. If you are interested in the Tracks that

have received the most attention during a specific period you can pass a

time interval from which the hotness is based. The time interval is set by the

independently optional bounding parameters created_at[from] and

created_at[to].

Assume that current time is 2008-03-13 18:00:00.

Hot tracks the last 24 hours, from 2008-03-12 18:00:00

/tracks?order=hotness&created_at[from]=2008-03-12+18%3A00%3A00

Yesterday’s hot tracks, from 2008-03-11 00:00:00, to 2008-03-12 00:00:00

/tracks?order=hotness&created_at[from]=2008-03-11+00%3A00%3A00&created_at[to]=2008-03-12+00%3A00%3A00

Hot tracks the last seven days, from 2008-03-06

/tracks?order=hotness&created_at[from]=2008-03-06

Hot tracks this month, from 2008-03-01

/tracks?order=hotness&created_at[from]=2008-03-01

Hot tracks this year, from 2008-01-01

/tracks?order=hotness&created_at[from]=2008-01-01

The hottest tracks of all time,

/tracks?order=hotness

53

POST /tracksResponse headers: Location – Canonical URI of the track

Parameter Name Value Description

track[title] string the title of the track

track[asset_data] file the original asset file

track[description] string a description

track[downloadable] true | false

track[sharing] public | private

track[bpm] float beats per minute

Creates a track belonging to the logged in user. Since a file asset is attached

the request must be a ‘multipart/form-data’ request, hence we can’t send

data in XML-format.

$ curl -u joey:hairspray 'http://api.sandbox-soundcloud.com/tracks' -F track[asset_data][email protected] -F track[title]=Superstitious

< HTTP/1.1 201 Created< Location: http://api.soundcloud.com/tracks/13

GET /tracks/{track-id}Result type: Track

Retrieves the given track.

$ curl 'http://api.soundcloud.com/tracks/13'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><track> <id type="integer">13</id> <permalink>superstitious</permalink> <title>Superstitious</title> ... <sharing>public</sharing></track>

PUT /tracks/{track-id}Updates the given track.

54

Parameter Name Value Description

track[title] string Title of the track

track[description] string Description of the track

track[downloadable] true | false

track[sharing] public | private

track[bpm] float Beats per minute

$ curl 'http://api.soundcloud.com/tracks/13' -X PUT \-H 'Content-Type: application/xml' -d \'<track> <downloadable>true</downloadable ></track>'

< HTTP/1.1 200 OK

DELETE /tracks/{track-id}Deletes the given track

$ curl 'http://api.soundcloud.com/tracks/13' -X DELETE

< HTTP/1.1 200 OK

GET /tracks/{track-id}/commentsType: Collection of Comments

Retrieves comments made on the given track.

$ curl 'http://api.soundcloud.com/tracks/9/comments'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><comments type="array"> <comment> <body>A personal favorite of mine.</body> <id type="integer">2</id> <timestamp type="integer" nil="true"></timestamp> <track-id type="integer">9</track-id> <user-id type="integer">9</user-id> <uri>http://api.soundcloud.com/comments/2</uri> <created-at type="datetime">2008-08-06T21:46:38+02:00</created-at> </comment></comments>

55

POST /tracks/{track-id}/commentsResponse Headers: Location – The canonical URI of the comment

Creates a comment on the given track.

Parameter Name Value Description

comment[body] string

comment[timestamp] integer milliseconds

comment[reply_to] integer if the comment is a reply to another comment, specify the other commentʼs id

$ curl 'http://api.soundcloud.com/tracks/1/comments' -X POST \-H 'Content-Type: application/xml' -d \'<comment> <body>Yeah!</body></comment >'

< HTTP/1.1 201 Created< Location: http://api.soundcloud.com/comments/13

GET /tracks/{track-id}/permissionsType: Collection of Users

Retrieves the users with permission to see the track.

$ curl 'http://api.soundcloud.com/tracks/10/permissions'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><users type="array"> <user> <id type="integer">8</id> <username>jsb</username> ... </user> <user> <id type="integer">10</id> <username>vice</username> ... </user></users>

56

PUT /tracks/{track-id}/permissionsUpdates the list of permitted users.

Parameter Name Value Description

permissions[user_id][] integer

$ curl 'http://api.soundcloud.com/tracks/12/permissions' -X PUT \-H 'Content-Type: application/xml' -d \'<permissions> <user-id>2</user-id> <user-id>3</user-id></permissions>'

is equivalent to:

$ curl 'http://api.soundcloud.com/tracks/12/permissions' -X PUT -d 'permissions[user_id][]=2&permissions[user_id][]=3'

< HTTP/1.1 200 OK

GET /tracks/{track-id}/downloadType: Binary

Retrieves the original uploaded asset of the Track if it is downloadable.

$ curl 'http://api.soundcloud.com/tracks/1/download'

< HTTP/1.1 200 OK< Content-Transfer-Encoding: binary< Content-Disposition: attachment; filename="final_countdown.wav" < Content-Type: audio/x-wav

57

playlists (sets)Sets are internally called playlists due to naming restrictions and will

therefore be named thereafter in the rest of this document.

It’s only possible to read and update sets. Creating, and deleting will be

implemented shortly.

Resource Methods

/playlists GET

/playlists/{playlist-id} GET, PUT

GET /playlistsType: Collection of Sets

Collection of all sets.

Parameter Name Value Description

filter all | public | private

q string Search terms

Unauthenticated request:

$ curl 'http://api.soundcloud.com/playlists'

<?xml version="1.0" encoding="UTF-8"?><playlists type="array"> <playlist> <created-at type="datetime">2008-07-22T13:28:59+02:00</created-at> <description/> <genre/> <id type="integer">315</id> ... <duration>538320</duration> </playlist></playlists>

58

Authenticated request:

Generates the same response but the sets returned only contain the tracks

that the authenticated user has the right to access.

The collection is searchable by passing the query to the resource. It then

lists a subset matching the search query.

$ curl 'http://api.soundcloud.com/playlists?q=ice'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><playlists type="array"> <playlist> <created-at type="datetime">2008-07-22T13:28:59+02:00</created-at> <description/> <genre/> <id type="integer">315</id> ... <duration>538320</duration> </playlist></playlists>

59

GET /playlists/{playlist-id}Result type: Set

Retrieves the given set.

$ curl 'http://api.soundcloud.com/playlists/315'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><playlist> <created-at type="datetime">2008-07-22T13:28:59+02:00</created-at> <description/> <genre/> <id type="integer">315</id> ... <duration>538320</duration> <tracks> <track> <id>12</id> ... </track> <track> <id>13</id> ... </track> <track> <id>14</id> ... </track> </track></playlist>

60

PUT /playlist/{playlist-id}Updates the given playlist.

Parameter Name Value Description

playlist[title] string

playlist[description] string

playlist[tracks][][id] integer Ordered list of track ids

The example below updates the playlist with a new title and reorders the

tracks from the GET /playlists/{playlist_id} example.

$ curl 'http://api.soundcloud.com/playlists/315' -X PUT \-H 'Content-Type: application/xml' -d \'<playlist> <title>Best of Bach</title> <tracks> <track> <id>14</id> </track> <track> <id>13</id> </track> <track> <id>12</id> </track> </track></playlist>'

< HTTP/1.1 200 OK

61

comments

Resource Methods

/comments/{comment-id} GET, DELETE

GET /comments/{comment-id}Type: Comment

Retrieves the given comment.

$ curl 'http://api.soundcloud.com/tracks/9/comments'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><comments type="array"> <comment> <body>A personal favorite of mine.</body> <id type="integer">2</id> <timestamp type="integer" nil="true"></timestamp> <track-id type="integer">9</track-id> <user-id type="integer">9</user-id> </comment></comments>

DELETE /comments/{comment-id}Deletes the given comment.

This is only allowed to the comment’s creator or the associated track’s

owner.

$ curl 'http://api.soundcloud.com/comments/1' -X DELETE

< HTTP/1.1 200 OK

62

events

Resource Methods

/events GET

/events/{event-id} GET

GET /eventsType: Collection of Events

Retrieves a list of the logged in user’s events.

Parameter Name Value Description

filter all | comment | track | shared_to | drop | favorite | fan

Event type

$ curl 'http://api.soundcloud.com/events'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><events type="array"> <event> <created-at type="datetime">2008-04-04T11:05:15+02:00</created-at> <id type="integer">4</id> <resource_id type="integer">1</id> <type>Comment</type> <comment> <id type="integer">1</id> ... </comment> </event> <event> <created-at type="datetime">2008-04-03T11:05:15+02:00</created-at> <id type="integer">4</id> <resource_id type="integer">1</id> <type>Favorite</type> <track> <id type="integer">1</id> ... </track> </event></events>

63

DropBoxTo retreiving the tracks in your dropbox you simply filter your events

collection on the ‘drop’ type

$ curl 'http://api.soundcloud.com/events?filter=drop'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><events type="array"> <event> <created-at type="datetime">2008-04-04T11:05:15+02:00</created-at> <id type="integer">4</id> <resource_id type="integer">1</id> <type>Drop</type> <track> <id type="integer">1</id> <sharing>private</sharing> <uri>http://api.soundcloud.dev/tracks/1</uri> <title>For you</title> ... </track> </event> ...</events>

64

GET /events/{event_id}Shows the given event and its related resource.

Type: Event

$ curl 'http://api.soundcloud.com/event/1'

< HTTP/1.1 200 OK

<?xml version="1.0" encoding="UTF-8"?><event> <created-at type="datetime">2008-04-03T11:05:15+02:00</created-at> <id type="integer">4</id> <resource_id type="integer">1</id> <type>Favorite</type> <track> <id type="integer">1</id> ... </track></event>

65

TRITA-CSC-E 2011:047 ISRN-KTH/CSC/E--11/047-SE

ISSN-1653-5715

www.kth.se