NAV Navbar
shell python

Introduction

Welcome to the Larder API. Using this API you can create read/write clients for both your own and your users’ bookmarks.

Getting started

  1. Check out the authentication section below to determine if you need to grab a simple one-off token, or create an OAuth2 client for many users.
  2. Create a client or grab your simple token from the site, then use your new credentials to start requesting authenticated endpoints.
  3. Need help? You can always email us at hello@larder.io if you’re stuck or need to clarify something.

Quick tips:

Authentication

There are two authentication methods — simple tokens, and OAuth2 clients. So which one do you need?

Simple token authentication exists as a basic means for users to access their own data from Larder. This is only recommended for quickly building a single-user client for yourself where OAuth2 is overkill. If you intend to ask other users to authenticate with your client, do not use this method — that’s what OAuth2 is for.

OAuth2 clients are superior to simple-token authentication when authenticating many users. One client application can create and use access tokens for many users. One user may have many clients authorised to access their Exist account, each with a separate token that can be revoked.

You can create a client from your app management page within your account.

Simple token authentication

All endpoints require authentication. For quick personal hacking purposes we provide a simple token-based scheme with a single token per user. Make sure this token is included in your requests by including the Authorization: Token [token] header with every request.

If you are logged in to Larder in the browser your session-based authentication will also work. This is handy for browsing the API (assuming you’ve set up your browser to accept JSON) but shouldn’t be relied on for programmatic access.

Your token is displayed on your app management page. You cannot exchange user credentials for a token with this method — if you’d like to make a client for others to use, please use OAuth2.

Signing requests

Include the “Authorization: Token xyz” header in all requests.

import requests

requests.post(url,
    headers={'Authorization':'Token 96524c5ca126d87eb18ee7eff408ca0e71e94737'})
# With curl, you can just pass the correct header with each request
curl "api_endpoint_here"
  -H "Authorization: Token 96524c5ca126d87eb18ee7eff408ca0e71e94737"

Include the Authorization: Token [your_token] header in all requests.

OAuth2 authentication

Make sure you have created a client within Larder, so you’ve received client credentials. You can create a client from your app management page within your account.

All endpoints require authentication, except those that are part of the OAuth2 authorisation flow. Make sure your access token is included in your requests by including the Authorization: Bearer [token] header with every request.

You may mix and match OAuth2 authentication with simple token or even session-based authentication as you test the API. API endpoints will respond with JSON within your browser if you are logged in to the site.

Authorisation flow

Send your user to the authorisation page at https://larder.io/oauth/authorize/

# We can't really do this from the shell, but your URL would look like this:

curl https://larder.io/oauth/authorize/?response_type=code&client_id=[your_id]&redirect_uri=[your_uri]&scope=[your_scope]
# in django, we would do something like this
return redirect('https://larder.io/oauth/authorize/' 
    '?response_type=code&client_id=%s&redirect_uri=%s&scope=%s' % ( 
    CLIENT_ID, REDIRECT_URI,"read+write"))

User authorises your client by hitting ‘Allow’, and Larder returns the user to your redirect_uri with code=[some_code] in the query string. Exchange your code for an access token:

curl -X POST https://larder.io/oauth/token/ -d "grant_type=authorization_code" \ 
  -d "code=[some_code]" -d "client_id=[your_id]" -d "client_secret=[your_secret]" \ 
  -d "redirect_uri=[your_uri]"
import requests

url = 'https://larder.io/oauth/token/'

response = requests.post(url,
           {'grant_type':'authorization_code',
            'code':code,
            'client_id':CLIENT_ID,
            'client_secret':CLIENT_SECRET,
            'redirect_uri':REDIRECT_URI })

Returns JSON if your request was successful:

{ "access_token": "122bb8707b6aee134e7746a40feca41868ddd578", 
  "token_type": "Bearer", 
  "expires_in": 2591999, 
  "refresh_token": "ac45027ad037f53b3ce91be272b163f55a4a87e9", 
  "scope": "read write read+write" 
}

The OAuth2 authorisation flow is a bit of a pain, but still vastly simpler than the original OAuth 1.0.

  1. Send your user to the “request authorisation” page at /oauth/authorize/ with these parameters in the query string:
    • response_type=code to request an auth code in return
    • redirect_uri with the URI to which Larder returns the user (must be HTTPS)
    • scope=read or scope=read+write to request read or read/write permissions
    • client_id which is your OAuth2 client ID
  2. User authorises your application within the requested scopes (by hitting 'Allow’ in the browser)
  3. Larder returns the user to your redirect_uri (as a GET request) with the following:
    • code parameter upon success
    • error parameter if the user didn’t authorise your client, or any other error with your request
  4. Exchange this code for an access token by POSTing to /oauth/token/ these parameters (use form data, not JSON):
    • grant_type=authorization_code
    • code with the code you just received
    • client_id with your OAuth2 client ID
    • client_secret with your OAuth2 client secret
    • redirect_uri with the URI you used earlier
  5. If successful you will receive a JSON object with an access_token, refresh_token, token_type, scope, and expires_in time in seconds.

Refreshing an access token

curl -X POST https://larder.io/oauth/token/ -d "grant_type=refresh_token" \ 
 -d "refresh_token=[token]" -d "client_id=[your_id]" -d "client_secret=[your_secret]"
import requests

url = 'https://larder.io/oauth/token/'

response = requests.post(url,
           {'grant_type':'refresh_token',
            'refresh_token':token,
            'client_id':CLIENT_ID,
            'client_secret':CLIENT_SECRET 
           })

Returns JSON if your request was successful:

{ "access_token": "122bb8707b6aee134e7746a40feca41868ddd578", 
  "token_type": "Bearer", 
  "expires_in": 2591999, 
  "refresh_token": "ac45027ad037f53b3ce91be272b163f55a4a87e9", 
  "scope": "read write read+write" 
}

Tokens expire in a month and can be refreshed for a new access token at any time, invalidating the original access and refresh tokens.

Request

POST /oauth2/access_token

Parameters

These must be regular form data, not JSON.

Name Description
refresh_token The refresh token previously received in the auth flow
grant_type refresh_token
client_id Your OAuth2 client ID
client_secret Your OAuth2 client secret

Response

The same as your original access token response, a JSON object with an access_token, refresh_token, token_type, scope, and expires_in time in seconds.

Signing requests

import requests

requests.post(url,
    headers={'Authorization':'Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737'})
# With curl, you can just pass the correct header with each request
curl "https://larder.io/api/1/@me/user/" \
  -H "Authorization: Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737"

Sign all authenticated requests by adding the Authorization header, Authorization: Bearer [access_token]. Note that this differs from the simple token-based authentication by using Bearer, not Token.

User


import requests

requests.get("https://larder.io/api/1/@me/user/",
    headers={'Authorization':'Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737'})

Returns JSON:

{
    "id": 100,
    "username": "josh",
    "first_name": "Josh",
    "last_name": "Sharp",
    "timezone": "Australia/Melbourne",
    "avatar": "https://larder.io/static/media/avatars/josh.png",
    "date_joined": "1992-01-10T03:55:09Z",
    "links": 1337,
    "is_trial": false
}

Get the authenticated user’s details.

GET https://larder.io/api/1/@me/user/

Folders

Get all folders

import requests

requests.get("https://larder.io/api/1/@me/folders/",
    headers={'Authorization':'Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737'})
curl "https://larder.io/api/1/@me/folders/" -H "Authorization: Bearer xyz"

Returns JSON:

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": "cf44505b-b235-4038-9d8a-3070489e97c8",
            "name": "Code",
            "color": "58db4e",
            "icon": null,
            "created": "2016-02-16T09:54:39Z",
            "modified": "2017-02-11T12:02:07Z",
            "parent": "42d1a97e-8e1b-420f-b51c-a49a69cb183a",
            "folders": [],
            "links": 19
        }
    ]
}

This returns a paged results of all folders for the authenticated user.

HTTP Request

GET https://larder.io/api/1/@me/folders/

Parameters

Name Description
limit Optional number of folders to return per page. Default 20.
offset Number of previous items to offset the returned page by. Optional, use in conjunction with limit.

Response

Returns a paged list of folders. Each folder contains the ID of its parent folder, and its child folders, so you can assemble the tree structure of folders. Folders may have a null parent which denotes them as top-level.

Get a specific folder

See get all bookmarks for a folder.

Add a folder

Creates a new folder where you can keep some good bookmarks.

HTTP Request

POST https://larder.io/api/1/@me/folders/add/

Parameters

Name Description
name String (max 120 chars) representing the folder’s name.
parent UUID (the id field) of the folder that’ll be the parent of this one. Send this as null for a top-level folder.

Response

Returns a HTTP 201 status and the saved JSON folder object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON object with an error field detailing the error. You may also get a 404 if the parent ID is incorrect.

Edit a folder

Updates the fields on a folder.

HTTP Request

POST https://larder.io/api/1/@me/folders/:id/edit/

Parameters

Name Description
name String (max 120 chars) representing the folder’s name.
parent UUID (the id field) of the folder that’ll be the parent of this one. Send this as null for a top-level folder.

Response

Returns a HTTP 200 status and the saved JSON folder object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON object with an error field detailing the error. You may also get a 404 if the parent ID is incorrect.

Delete a folder

Removes a folder entirely, and optionally its contents too. It’s all really truly deleted from the database, so beware. You may choose to move the contents of the folder to another folder.

HTTP Request

POST https://larder.io/api/1/@me/folders/:id/delete/

Parameters

Name Description
empty_to UUID of the folder you’d like to move the contents of this folder to. This is required but can be explicitly set to null if you’re sending JSON, or an empty string for x-www-form-urlencoded data, in which case bookmarks will be deleted.

Response

Returns a HTTP 204 status and no content if all went well. Otherwise you’ll get a HTTP 404 status if it couldn’t be found.

Bookmarks

Get all bookmarks for a folder

import requests

requests.get("https://larder.io/api/1/@me/folders/cf44505b-b235-4038-9d8a-3070489e97c8/",
    headers={'Authorization':'Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737'})
curl "https://larder.io/api/1/@me/folders/cf44505b-b235-4038-9d8a-3070489e97c8/" \ 
  -H "Authorization: Bearer xyz"

Returns JSON:

{
    "count": 19,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": "2fac2637-a4ab-4b03-be56-224d3608742f",
            "tags": [
                {
                    "id": "c1e3ced3-2702-4b49-a771-2d070d3b1c0e",
                    "name": "java",
                    "color": "8fcb00",
                    "created": "2016-02-12T11:41:41Z",
                    "modified": "2016-05-14T07:53:48Z"
                }
            ],
            "title": "square/picasso",
            "description": "A powerful image downloading and caching library for Android",
            "url": "https://github.com/square/picasso",
            "domain": "github.com",
            "created": "2016-02-12T12:10:39Z",
            "modified": "2016-12-01T05:44:18Z",
            "meta": {
                "commit": {
                    "date": "2016-11-01T13:51:32Z",
                    "message": "Merge pull request #1522 from NightlyNexus/eric/remoteviews-callback",
                    "author": "JakeWharton"
                },
                "type": "github.com",
                "release": {
                    "description": "Initial release.",
                    "name": "picasso-parent-1.0.0",
                    "published_at": "2013-08-30T23:28:03Z",
                    "author": "JakeWharton"
                }
            }
        }
    ]
}

Requesting a specific folder returns a paged list of the folder’s bookmark contents.

HTTP Request

GET https://larder.io/api/1/@me/folders/:id/

Parameters

Name Description
limit Optional number of bookmarks to return per page. Default 20.
offset Number of previous items to offset the returned page by. Optional, use in conjunction with limit.

Response

Returns a paged list of bookmarks. Each will contain a list of tags related to this bookmark. The meta field contains metadata related to the type of bookmark. In the example, a GitHub starred repo that’s been imported, the metadata contains recent commit and release information. For many bookmarks meta will just be null.

Add a bookmark

curl "https://larder.io/api/1/@me/links/add/" \ 
  -H "Authorization: Bearer 8448136eaacd2829821e0c350e816b44e1607529" \ 
  -X POST -d "url=https://github.com/square/picasso" -d "title=square/picasso"
  -d "parent=f2ac2637-aa4b-4b03-be56-224d3008742a" -d "tags=java"
import requests
import json

bookmark = {
    'parent':'f2ac2637-aa4b-4b03-be56-224d3008742a',
    'title': 'square/picasso',
    'url': 'https://github.com/square/picasso',
    'tags': ['java']
}

response = requests.post("https://larder.io/api/1/@me/links/add/",
    data=json.dumps(bookmark),
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})

Returns JSON bookmark object on success:

{
    "id": "2fac2637-a4ab-4b03-be56-224d3608742f",
    "parent": {
        "id": "f2ac2637-aa4b-4b03-be56-224d3008742a",
        "name": "Android",
        "color": "cb8fa0",
        "created": "2016-02-12T10:40:00Z",
        "modified": "2016-05-14T07:53:48Z"
    },
    "tags": [
        {
            "id": "c1e3ced3-2702-4b49-a771-2d070d3b1c0e",
            "name": "java",
            "color": "8fcb00",
            "created": "2016-02-12T11:41:41Z",
            "modified": "2016-05-14T07:53:48Z"
        }
    ],
    "title": "square/picasso",
    "description": null,
    "url": "https://github.com/square/picasso",
    "domain": "github.com",
    "created": "2016-12-01T12:44:18Z",
    "modified": "2016-12-01T12:44:18Z",
    "meta": null
}

Arguably the most important endpoint in the entire API.

HTTP Request

POST https://larder.io/api/1/@me/links/add/

Parameters

Name Description
title Optional string (max 120 chars) representing the page’s title. Defaults to the URL field if left empty.
url String of the URL you’d like to save.
parent UUID (the id field) of the folder that’ll hold this bookmark.
tags Required (but can be empty) array of tag names, eg. tags: ["json","python"]. If any don’t exist they will be created.
description Optional string (max 160 chars) where you might add a little note about the bookmark.

If you use application/x-www-form-urlencoded parameters you can build up tags by adding the key multiple times, eg. tags=python&tags=web. In JSON just send these as an array.

Response

Returns a HTTP 201 status and the saved JSON bookmark object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON object with an error field detailing the error. You may also get a 404 if the parent ID is incorrect.

If your user’s trial has ended, you’ll receive a HTTP 402 status code with the response: {"error": "Trial ended: cannot add new bookmarks."}

Edit a bookmark

curl "https://larder.io/api/1/@me/links/2fac2637-a4ab-4b03-be56-224d3608742f/edit/" \ 
  -H "Authorization: Bearer 8448136eaacd2829821e0c350e816b44e1607529" \ 
  -X POST -d "url=https://github.com/square/picasso" -d "title=square/picasso"
import requests

response = requests.post("https://larder.io/api/1/@me/links/%s/edit/" % bookmark_id,
    data={'parent':'f2ac2637-aa4b-4b03-be56-224d3008742a'},
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})

Returns JSON bookmark object on success:

{
    "id": "2fac2637-a4ab-4b03-be56-224d3608742f",
    "parent": "f2ac2637-aa4b-4b03-be56-224d3008742a",
    "tags": [
        {
            "id": "c1e3ced3-2702-4b49-a771-2d070d3b1c0e",
            "name": "java",
            "color": "8fcb00",
            "created": "2016-02-12T11:41:41Z",
            "modified": "2016-05-14T07:53:48Z"
        }
    ],
    "title": "square/picasso",
    "description": null,
    "url": "https://github.com/square/picasso",
    "domain": "github.com",
    "created": "2016-12-01T12:44:18Z",
    "modified": "2016-12-01T12:44:18Z",
    "meta": null
}

Update a bookmark’s fields.

HTTP Request

POST https://larder.io/api/1/@me/links/:id/edit/

Parameters

You may send only the fields you wish to update. Any fields not sent will remain unchanged.

Name Description
title Optional string (max 120 chars) representing the page’s title. Defaults to the URL field if left empty.
url String of the URL you’d like to save.
parent UUID (the id field) of the folder that’ll hold this bookmark.
description Optional string (max 160 chars) where you might add a little note about the bookmark.
tags Optional array of tag names, eg. tags: ["json","python"]. If any don’t exist they will be created.

If you use application/x-www-form-urlencoded parameters you can build up tags by adding the key multiple times, eg. tags=python&tags=web. In JSON just send these as an array. An empty array will clear existing tags.

Response

Returns a HTTP 200 status and the saved JSON bookmark object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON array or object of errors. You may also get a 404 if the parent ID is incorrect.

If your user’s trial has ended, you’ll receive a HTTP 402 status code with the response: {"error": "Trial ended: cannot add new bookmarks."}

Delete a bookmark

curl "https://larder.io/api/1/@me/links/2fac2637-a4ab-4b03-be56-224d3608742f/delete/" \ 
  -X POST -H "Authorization: Bearer 8448136eaacd2829821e0c350e816b44e1607529" 

Delete a bookmark permanently.

HTTP Request

POST https://larder.io/api/1/@me/links/:id/delete/

Response

Returns a HTTP 204 status and no content if all went well. Otherwise you’ll get a HTTP 404 status if it couldn’t be found.

Tags

Get all tags

import requests

requests.get("https://larder.io/api/1/@me/tags/",
    headers={'Authorization':'Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737'})
curl "https://larder.io/api/1/@me/tags/" -H "Authorization: Bearer xyz"

Returns JSON:

{
    "count": 260,
    "next": "https://larder.io/api/1/@me/tags/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "id": "9252e320-cc28-415f-b1f6-be28a90d07ed",
            "name": "algorithm",
            "color": "00bfcb",
            "created": "2016-02-25T06:39:09Z",
            "modified": "2016-05-14T07:53:51Z"
        },
        {
            "id": "2c1bc264-e557-42be-a83b-2bfe53f1cd9c",
            "name": "analysis",
            "color": "8c5b6c",
            "created": "2016-02-25T06:39:08Z",
            "modified": "2016-05-14T07:53:50Z"
        }
    ]
}    

Requesting a specific folder returns a paged list of the folder’s bookmark contents.

HTTP Request

GET https://larder.io/api/1/@me/tags/

Parameters

Name Description
limit Optional number of tags to return per page. Default 20.
offset Number of previous items to offset the returned page by. Optional, use in conjunction with limit.

Response

Returns a paged list of tags.

Add a tag

curl "https://larder.io/api/1/@me/tags/add/" \ 
  -X POST -d "name=algorithm" \ 
  -H "Authorization: Bearer 8448136eaacd2829821e0c350e816b44e1607529" 
import requests

response = requests.post("https://larder.io/api/1/@me/tags/add/",
    data={'name':'algorithm'},
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})

Returns the JSON tag object if successful:

{
    "id": "9252e320-cc28-415f-b1f6-be28a90d07ed",
    "name": "algorithm",
    "color": "00bfcb",
    "created": "2016-02-25T06:39:09Z",
    "modified": "2016-02-25T06:39:09Z"
}

Creates a new tag object you may later “tag” some good bookmarks with.

HTTP Request

POST https://larder.io/api/1/@me/tags/add/

Parameters

Name Description
name String (max 64 chars) representing the tag’s name. This must be a 'slug’ field meaning only alphanumeric characters, underscores, and hyphens.

Response

Returns a HTTP 201 status and the saved JSON tag object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON object with fields detailing any errors.

Edit a tag

import requests

response = requests.post("https://larder.io/api/1/@me/tags/%s/edit/" % tag_id,
    data={'name':'algorithms'},
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})
curl "https://larder.io/api/1/@me/tags/9252e320-cc28-415f-b1f6-be28a90d07ed/edit/" \ 
  -H "Authorization: Bearer 8448136eaacd2829821e0c350e816b44e1607529" \ 
  -X POST -d "name=algorithms"

Returns JSON tag object if successful:

{
    "id": "9252e320-cc28-415f-b1f6-be28a90d07ed",
    "name": "algorithms",
    "color": "00bfcb",
    "created": "2016-02-25T06:39:09Z",
    "modified": "2016-05-14T07:53:51Z"
}

Rename a tag you previously put a typo in.

HTTP Request

POST https://larder.io/api/1/@me/tags/:id/edit/

Parameters

Name Description
name String (max 64 chars) representing the tag’s name. This must be a 'slug’ field meaning only alphanumeric characters, underscores, and hyphens.

Response

Returns a HTTP 200 status and the saved JSON tag object if all went well. Otherwise you’ll get a HTTP 400 status and a JSON object with fields detailing any errors.

Delete a tag

curl "https://larder.io/api/1/@me/tags/2faca637-a4ab-4b03-be56-2a4d3608742f/delete/" \ 
  -X POST -H "Authorization: Bearer 1ffb9306d260981da0a2a1057cebdcad" 
import requests

response = requests.post("https://larder.io/api/1/@me/tags/%s/delete/" % tag_id, 
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})

Delete a tag permanently. Any bookmarks with this tag attached just get that tag removed.

HTTP Request

POST https://larder.io/api/1/@me/tags/:id/delete/

Response

Returns a HTTP 204 status and no content if all went well. Otherwise you’ll get a HTTP 404 status if it couldn’t be found.

Searching

You can find bookmarks by searching with a query. This query string will be matched against bookmark titles, URLs, descriptions, and tags.

curl "https://larder.io/api/1/@me/search/?q=python" \
 -H "Authorization: Bearer 1ffb9306d260981da0a2a1057cebdcad"
import requests

response = requests.get("https://larder.io/api/1/@me/search/", 
    params={'q':'python'},
    headers={'Authorization':'Bearer 1ffb9306d260981da0a2a1057cebdcad'})
print(response.json())

Returns a paged set of JSON results:

{
    "count": 217,
    "next": "http://larder.athena.lan/api/1/@me/search/?limit=20&offset=20&q=python",
    "previous": null,
    "results": [
        {
            "id": "a53f271a-c3b0-4d49-b6f1-9b532b3b0dcc",
            "parent": {
                "id": "42d1a97e-8e5b-420f-b50c-a49a69cb183a",
                "name": "Coding",
                "color": "289ef0",
                "icon": "",
                "created": "2016-01-10T03:56:16Z",
                "modified": "2017-02-11T12:02:07Z",
                "parent": null
            },
            "tags": [
                {
                    "id": "f98c7bd5-747d-4cd7-9b16-300f26ef67f6",
                    "name": "python",
                    "color": "0099cb",
                    "created": "2016-01-10T03:57:21Z",
                    "modified": "2016-05-14T07:53:46Z"
                }
            ],
            "title": "kennethreitz/requests",
            "description": "Python HTTP Requests for Humans™",
            "url": "https://github.com/kennethreitz/requests",
            "domain": "github.com",
            "created": "2016-12-01T05:32:57Z",
            "modified": "2016-12-01T05:38:44Z",
            "meta": {
                "commit": {
                    "author": "Lukasa",
                    "date": "2016-11-30T12:38:26Z",
                    "message": "v2.12.2"
                },
                "type": "github.com"
            }
        }
    ]
}

HTTP Request

GET https://larder.io/api/1/@me/search/

URL Parameters

Parameter Description
q The string to search for. This may start with # to find results tagged with exactly this tag, for example ?q=#python returns only bookmarks with the tag python.