Springpad wouldn't be possible without the rich set of APIs that already exist on the web. And we're serious about providing open APIs to allow users to export their data for backup, to sync with their other favorite services, or to take advantage of their Springpad data in innovative ways provided by other companies. To that end, we're releasing the Springpad Beta API. This is the same API we use with our mobile platforms and it will enable developers to build applications that can both get data from Springpad and send commands to modify existing Springpad data. We look forward to hearing your feedback on the API and can't wait see what you do with it!
Springpad allows users to quickly capture ideas, chores, or things that they want to save for later. At its most basic, this means taking a note or adding a to-do, but Springpad also makes it easy to capture structured data such as books, restaurants, recipes, and products. This means that if a user has saved a book that he wants to remember, he can find it in the system not only by searching for it but also by browsing for it. Springpad's data structures allow the book to be found based on it being a book, by its genre, or even by price or number of pages. This kind of rich data is available for all of the different data types, in addition to details the user can add to it, such as whether they "have read" the book or "want to read" it or how he rates it.
In addition to making it easy to find things that the user has saved, Springpad also tries to add some additional value to the object. For example, Springpad automatically adds links to reviews of books and movies, links to Yelp and Open Table for restaurants, or price information for products. Further, Springpad has an alert system that delivers pertinent news and offers to users based on their data, such as price drops, coupons, and relevant news stories. All of this data is available to users on the web as well as via our mobile apps.
This document covers how to authenticate with Springpad, how to access, create, and modify objects in the system, how to get basic user information, and the data formats used by the system. Our example code for authentication is written in python and we provide a python API to make integration easier. The API is accessible to most languages since we rely on HTTP protocols, JSON, and OAuth. Before getting into the details of the API, we'll begin with a brief overview of the Springpad system and its datamodel to provide context for the rest of the document.
Every user in Springpad is represented by a user object. Users, as well as every other object in Springpad, are uniquely identified by a UUID. In addition to some information about the user, users have blocks. Generally speaking, blocks refer to the objects that users collect in their account such as notes, tasks, restaurants, etc. For a more precise understanding of the blocks, consider an analogy to an object-oriented type system. Springpad has types which are much like classes. Just like a class, Springpad types have properties, each with a name and a type. Also like classes, types can 'subclass' other types and inherit their properties. If Springpad types are like classes, then blocks are like instances. A block is instantiated from a type just as an instance is from a class. Knowing the type of the blocks, allows you to know what properties the block will have (e.g., a book will have a genre, author, price, etc.).
The specific properties of each type are detailed in the Type API section. First, we'll discuss how to connect to Springpad and interact with the system.
In this section, we discuss the Springpad API. It is a somewhat RESTful API that enables services to authenticate as a Springpad user and then query for their blocks, create new blocks, or modify existing ones. We begin by discussing authentication before moving into the rest of the API.
The beta release of the api is version 5.0. It is important to include the header parameter that identifies which version the request is being made with. The header name is X-Spring-Api-Version.
7-22-2011
Since Springpad deals in information that is personal to the user it is required that all API requests be made via https. Requests being made on http may be processed temporarily, however, this could compromise users data and we intend to limit this type of access in the near future. Avoiding such a compromise of data is of the utmost importance to us.
Any request to Springpad that modifies data or needs access to private information must successfully authenticate. Springpad supports a simple username and password-based authentication scheme which is intended only to be used for experimentation and one-off calls to the service. For all other uses, we support Oauth. Either authentication method requires that you request access for your application and send the token along with the request to identify your application to Springpad. We'll first discuss how to request the token and then each of the authentication methods in turn.
In order to access the Springpad API, you will need to request developer access. To register your application for access, visit our registration page. You will see this page:
The form contains the following elements:
When you click the 'Request a Key' button, you will be directed to page confirming that we've received your request. As soon as possible, we will review your request and grant access to the Springpad API. At this time, you'll receive the credentials you need to get started with the API. The email will look something like this:
This email provides you with your consumer key and consumer secret. Store these for future reference. For username/password authentication, you will use just the consumer key to identify your application to Springpad. Both are necessary for OAuth authentication. We will discuss each type of authentication in turn next.
For simple one-off requests or exploratory coding, we support a username/password-based method of authentication. To do this, simply include X-Spring-Username and X-Spring-Password in the header of each HTTP request to the server with the username and password for the account you are accessing. Additionally, include X-Spring-Api-Token in the header with your consumer key. Alternatively, you can pass the username or password in request parameters. This will not be permitted for a large volume of requests. Once you are ready to release something for public consumption, you will need to authenticate with OAuth.
OAuth is a widely-accepted, open protocol for secure authentication with webservice APIs. It not only provides a secure method of authentication but also safeguards the user's password from 3rd parties. There are several steps to authenticating with a server using OAuth, so we'll walk through the process step-by-step. Example code is also available in our python client library. In order to authenticate using OAuth, you need to request access to a user's data using your application identifiers (consumer key and consumer secret). Springpad will respond with a request token which you use to direct the user to a URL on the Springpad server that will allow them to grant your application access to their information. When they've granted it, you can then use the request token to get a permanent access token for that user. That token is then included in all API requests to identify the user. It is valid until the user revokes it.
The first thing your application needs to do to gain access to a user's Springpad data is to get a request token from the Springpad server. To perform OAuth authentication, you will need to use an OAuth library for your language, probably available here. Our examples will use the python implementation. Once you have downloaded and installed the oauth library, you simple need to send an HTTP GET request to http://springpad.com/developers/oauth/register-app with a request signed with your consumer key and consumer secret.
In the following code, we create an HTTP GET request to get the request-token from Springpad and then parse the response saving the token in the request_token variable.
import oauth.oauth as oauth import httplib2 CONSUMER_KEY = '72c99068dbff4bc68a8d98eb4b17d734' CONSUMER_PRIVATE = '9313227b97244a76bd0508322dd61f60' consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_PRIVATE) request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=None, http_url="http://springpad.com/api/oauth-request-token", http_method='GET') signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() request.sign_request(signature_method, consumer, None) url = request.to_url() response, data = httplib2.Http().request(url) request_token = oauth.OAuthToken.from_string(data)
With the request token, now redirect the user to Springpad where they will be asked to grant your application access. In the following code sample, we create the URL to direct the user to.
url = "http://springpad.com/api/oauth-authorize%?s" % request_token # produces: http://springpad.com/api/oauth-authorize?oauth_token_secret=0e71505050ab9c685b7b85aa37cb95cd&oauth_token=d51e15710bfee0a680cf06325b8fcb27
When the user approves access to your service, Springpad will redirect the user to the callback URL you provided during the request process with the request token as a parameter. Once the callback URL is hit, you can request the access token from Springpad using the request token as the access token. In the following code sample, we create a request for the access token and then save the access token based on the response.
import oauth.oauth as oauth import httplib2 consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_PRIVATE) request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=request_token, http_url="http://springpad.com/api/oauth-access-token", http_method='GET') signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() request.sign_request(signature_method, consumer, request_token) url = request.to_url() response, data = httplib2.Http().request(url) access_token = oauth.OAuthToken.from_string(data)
Now that you've gotten the access token for the user, you simply need to sign each request much like you did in the previous step, except with the access token, rather than the request token. For example:
import oauth.oauth as oauth import httplib2 consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_PRIVATE) request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=access_token, http_url="http://springpad.com/api/users/me/blocks?limit=25&sort=created", http_method='GET') signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() request.sign_request(signature_method, consumer, access_token) url = request.to_url() response, data = httplib2.Http().request(url)
For all requests you can specify a jsonp parameter with a callback function name to use JSONP. This will wrap the output in a function call to the specified function. Either 'jsonp' or 'callback' will work as the parameter name.
This section describes the API for accessing blocks in the system, both those belonging to the user as well as those from other Springpad users and from the web.
Filters:
In this section, we will discuss the data format that the Springpad API uses. As has already been mentioned, the Springpad API uses JSON to format result sets. And HTTP POST data should also be formatted using JSON. The following section applies to those two cases. First, we will discuss the format of different types of values that you will receive from the Springpad. That format is also how you should send those values in your POST bodies. Then, we will show the different formats for result sets available from the API.
For the most part, the Springpad API uses the same format for values as JSON. So the numbers, strings, and arrays output by your JSON library will work seemlessly with Springpad. For example here is an array with some numbers and strings in it that will work with the Springpad API.
["here is a string", 23, "that was a number", 3.14, "that was another number", ""]
However, since there are no built-in standard Dates or UUIDs so we defined a protocol to reference them. We use a similar one to reference Springpad Types. To denote a date or datetime, simple create a string with the format: “/Date(t)/” where t = milliseconds since January 1, 1970, 00:00:00 GMT. For example:
"/Date(1272169340040)/"Similarly, UUIDs are strings formatted as: “/UUID (uuid)/” where uuid is the UUID’s string representation. Another important thing to note about UUIDs is that Springpad uses uses the first 3 characters of the UUID in specific ways. So if you are creating your own UUID for a new block be sure to attend to the following rules.
"/UUID(053d19eb-db67-4597-82bc-8a9ee48ff855)/"References to Springpad types are simply strings formatted as “/Type(typeName)/”. So the following example is a reference to the Note type.
"/Type(Note)/"You can request data from Springpad in four different formats. The data is always returned as JSON, but format selection determines how much data is returned from simply IDs to a full representation of the block based. This can enable efficient network-usage while accessing large volumes of data. We discuss each format in turn.
This is the most minimal format, simply returning a set of UUIDs for the blocks matched by the request. The UUID can be used to query for the block individually afterwards.
[{"uuid": "/UUID(05308a01-860f-49db-b33a-eb519c087c90)/"}, {"uuid": "/UUID(0530aa05-467b-4c2b-9480-16ce11eeb0cc)/"}]This format returns slightly more information, including the name and type of the Block as well some information about the owner.
[{"uuid": "/UUID(05308a01-860f-49db-b33a-eb519c087c90)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"created": "/Date(1270233039967)/",
"modified": "/Date(1272232317382)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"publicUrl": "restaurant/pizzamyheart",
"type": "/Type(Restaurant)/",
"name": "Pizza My Heart"},
{"uuid": "/UUID(0530aa05-467b-4c2b-9480-16ce11eeb0cc)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"created": "/Date(1272169340038)/",
"modified": "/Date(1272232260532)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"publicUrl": "restaurant/cafecolucci",
"type": "/Type(Restaurant)/",
"name": "Cafe Colucci"}]Preview is very similar to the minimal format. It adds a subtitle field. This was added to support being able to show a rich list view for the item without fetching the entire block.
[{"subtitle": "220 University Ave, Palo Alto, CA 94301",
"uuid": "/UUID(05308a01-860f-49db-b33a-eb519c087c90)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"image": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/05308a01-860f-49db-b33a-eb519c087c90/05308a01-860f-49db-b33a-eb519c087c90-perm-thumb.png",
"created": "/Date(1270233039967)/",
"modified": "/Date(1272232317382)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"services": ["yelp:6503279400", "sourceUrl:http%3A%2F%2Fwww.yelp.com%2Fbiz%2Fpizza-my-heart-palo-alto"],
"publicUrl": "restaurant/pizzamyheart",
"type": "/Type(Restaurant)/",
"name": "Pizza My Heart"},
{"subtitle": "6427 Telegraph Ave, Oakland, CA 94609",
"uuid": "/UUID(0530aa05-467b-4c2b-9480-16ce11eeb0cc)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"image": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/0530aa05-467b-4c2b-9480-16ce11eeb0cc/0530aa05-467b-4c2b-9480-16ce11eeb0cc-perm-thumb.png",
"created": "/Date(1272169340038)/",
"modified": "/Date(1272232260532)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"services": ["yelp:5106017999", "phone:5106017999", "sourceUrl:http%3A%2F%2Fwww.yelp.com%2Fbiz%2Fcafe-colucci-oakland"],
"publicUrl": "restaurant/cafecolucci",
"type": "/Type(Restaurant)/",
"name": "Cafe Colucci"}]This format includes all of the block information and nests any blocks belonging to it as properties.
[{"uuid": "/UUID(05308a01-860f-49db-b33a-eb519c087c90)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"created": "/Date(1270233039967)/",
"modified": "/Date(1272232317382)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"publicUrl": "restaurant/pizzamyheart",
"type": "/Type(Restaurant)/",
"properties": {
"userAction": "Done",
"services": ["yelp:6503279400", "sourceUrl:http%3A%2F%2Fwww.yelp.com%2Fbiz%2Fpizza-my-heart-palo-alto"],
"contactInfo": [{
"uuid": "/UUID(05300d53-fe17-45d6-9f2e-8ec895f355f9)/",
"created": "/Date(1270233039969)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1270233040080)/",
"type": "/Type(Address)/",
"properties": {
"city": "Palo Alto",
"zip": "94301",
"text": "220 University Ave, Palo Alto, CA 94301",
"state": "CA",
"street": "220 University Ave",
"lat": 37.444801330566399,
"lng": -122.16300201416
}
},
{
"uuid": "/UUID(05382fed-92f2-4c59-a11b-1cf4c9e9341d)/",
"created": "/Date(1270233040081)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1270233040084)/",
"type": "/Type(Account)/",
"properties": {
"username": "http://www.yelp.com/biz/pizza-my-heart-palo-alto",
"type": "Website"
}
},
{
"uuid": "/UUID(053025b7-78de-410f-a5a5-985b7b4920f1)/",
"created": "/Date(1270233040084)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1270233040087)/",
"type": "/Type(Phone)/",
"properties": {
"number": "6503279400"
}
}],
"thumbKey": "05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/05308a01-860f-49db-b33a-eb519c087c90/05308a01-860f-49db-b33a-eb519c087c90-perm-thumb.png"
},
"name": "Pizza My Heart"},
{"uuid": "/UUID(0530aa05-467b-4c2b-9480-16ce11eeb0cc)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"created": "/Date(1272169340038)/",
"modified": "/Date(1272232260532)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"publicUrl": "restaurant/cafecolucci",
"type": "/Type(Restaurant)/",
"properties": {
"userAction": "Done",
"attachments": [{
"uuid": "/UUID(0534e7be-7012-4eb1-a4ab-84e14dc49437)/",
"creatorUsername": "aykroyd",
"creatorPicture": "http://springpad-user-data.s3.amazonaws.com/05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/053ec38f-41b9-44e3-910d-2421cb9a6e7a-thumb/upload-05-8a7cc6e31d931fe1011d972029565e4457981.tmp",
"created": "/Date(1272231770675)/",
"modified": "/Date(1272231770743)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"type": "/Type(Note)/",
"properties": {
"text": "Pretty awesome menu.",
"isAttachment": True,
"rich": True
},
"publicUrl": "note/unnamednote/d5b"
}],
"personalRating": 3.0,
"image": "http://static1.px.yelpcdn.com/bpthumb/lmtzQCPjI_BOGngu9wNrOg/ms",
"sources": ["yelp:http://www.yelp.com/biz/cafe-colucci-oakland"],
"services": ["yelp:5106017999", "phone:5106017999", "sourceUrl:http%3A%2F%2Fwww.yelp.com%2Fbiz%2Fcafe-colucci-oakland"],
"contactInfo": [{
"uuid": "/UUID(0537627a-4687-4778-b934-600d22b0f8c8)/",
"created": "/Date(1272169340039)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1272169340041)/",
"type": "/Type(Address)/",
"properties": {
"city": "Oakland",
"zip": "94609",
"text": "6427 Telegraph Ave, Oakland, CA 94609",
"state": "CA",
"street": "6427 Telegraph Ave",
"lat": 37.850723000000002,
"lng": -122.260447
}
},
{
"uuid": "/UUID(0534f154-a98d-4fac-b61a-12375ab34b82)/",
"created": "/Date(1272169340041)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1272169340043)/",
"type": "/Type(Account)/",
"properties": {
"username": "http://www.yelp.com/biz/cafe-colucci-oakland",
"type": "Website"
}
},
{
"uuid": "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/",
"created": "/Date(1272169340043)/",
"creator": "/UUID(0544ca2d-7fb2-433e-86af-2ff16b9c8f65)/",
"modified": "/Date(1272169340045)/",
"type": "/Type(Phone)/",
"properties": {
"number": "5106017999"
}
}],
"thumbKey": "05/0544ca2d-7fb2-433e-86af-2ff16b9c8f65/0530aa05-467b-4c2b-9480-16ce11eeb0cc/0530aa05-467b-4c2b-9480-16ce11eeb0cc-perm-thumb.png"
},
"name": "Cafe Colucci"}]This section discusses how to obtain different kinds of counts of blocks in a user's account.
{
"source_ms":
{
"netflix":1
},
"userAction_s":
{
"ToDo":1
},
"tag":
{
"favorites":1
},
"metadata-personalRating_d":
{
"0.0":32,
"4.0":1
},
"isInPast_b":
{
"false":16,
"true":1
},
"flagged_b":
{
"false":32,
"true":1
},
"workbook_ms":
{
"08377a3d-2c33-42ce-897b-10c189854d76":1,
"0839b864-b572-4e14-9fd0-4ef0c5fc78f9":1
},
"complete_b":
{
"false":15
},
"cast_ms":
{
"Al Pacino":1,
"Diane Keaton":1,
"Marlon Brando":1,
"Richard Castellano":1,
"Robert Duvall":1
},
"type":
{
"lifemanagr.Task":15,
"lifemanagr.Note":14,
"lifemanagr.Appointment":2,
"lifemanagr.Movie":1,
"lifemanagr.GeneralList":1
},
"metadata-public_b":
{
"true":1,
"false":32
},
"directors_ms":
{
"Francis Ford Coppola":1
}
}This section discusses how to modify or create blocks in a user's account.
["create", {typename}, {uuid}]["create", "Restaurant", "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/"]
["delete", {uuid}]["set", {uuid}, {propertyName}, {value}][["set", "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/", "name", "My Favorite Book"], ["set", "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/", "date", "/Date(1272169340040)/"], ["set", "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/", "relatedBlock", "/UUID(053d19eb-db67-4597-82bc-8a9ee48ff855)/"], ["set", "/UUID(053d19eb-db67-4597-82bc-8a9ee48f1855)/", "number", 42.0]]
["add", {uuid}, {propertyName}, {value}]["remove", {uuid}, {propertyName}, {value}]["spring", {id}, {isAttachment}]["move", {uuid}, {propertyName}, {value}, {toIndex}]