design and implementation of a web service api with attachments
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
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
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
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