NAV
shell python

Introduction

Welcome to the littlelogs API!

The API itself lives at https://littlelogs.co/api/1/. All requests (excepting OAuth authentication) use this as their base. Responses always return JSON.

To get started quickly you can use your browser, as the API supports session-based authentication. For example if you browse to https://littlelogs.co/api/1/logs/ you can view all public logs.

Authentication

Littlelogs uses OAuth2 to allow access to the API. You can register a new OAuth client app from the apps page of your account.

Once you have authenticated a user, you must authorise all requests to the API by including a header that looks like the following:

Authorization: Bearer NWRjMTA1Yjk3MWM2NzY0NWMwNjkyYzEy

Public vs confidential apps

When you’re creating a client app, you’ll be asked to choose which of these your app falls under. So which to choose?

Public clients are those that cannot reasonably expect to keep their client ID and secret secure, such as client-side Javascript applications, or native mobile apps whose contents could be dissembled. Confidential clients are generally server-side, and their code and client details are treated as secure.

In reality the only difference I can find in the OAuth2 spec is that confidential clients must send their client_secret during the authorisation flow, and public apps do not.

OAuth2 authorisation flow

Send your user to the authorisation page at https://littlelogs.co/oauth/authorize

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

curl https://littlelogs.co/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://littlelogs.co/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 Littlelogs 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://littlelogs.co/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://littlelogs.co/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": 31535999, "refresh_token": "ac45027ad037f53b3ce91be272b163f55a4a87e9", "scope": "read write read+write" }

The OAuth2 authorisation flow is vastly simpler than the original OAuth 1.0:

  1. Send your user to the “request authorisation” page at /oauth/authorize with these parameters:
    • response_type=code to request an auth code in return
    • redirect_uri with the URI to which littlelogs 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. Exist returns the user to your redirect_uri (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:
    • 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://littlelogs.co/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://littlelogs.co/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": 31535999, "refresh_token": "ac45027ad037f53b3ce91be272b163f55a4a87e9", "scope": "read write read+write" }

Tokens expire in a month and can be refreshed at any time, invalidating the previous refresh and access tokens.

Request

POST /oauth/token

Parameters

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 "api_endpoint_here"
  -H "Authorization: Bearer 96524c5ca126d87eb18ee7eff408ca0e71e94737"

Sign all authenticated requests by adding the Authorization header, Authorization: Bearer [access_token].

Requests

Paging lists

You can page through lists with limit and offset parameters, or by following the next and previous links within any paged response object.

Avoiding DELETE

If your client doesn’t support the DELETE method, you can pass _method=DELETE in a POST request to achieve the same result.

Timelines

Home timeline

{

    "count": 1291,
    "next": "https://littlelogs.co/api/1/timelines/home/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "id": 2614,
            "created": "2015-11-02T00:23:27Z",
            "user": {        
                    "id": 2,
                    "username": "belle",
                    "first_name": "Belle Beth",
                    "last_name": "Cooper",
                    "bio": "I'm a bit silly.",
                    "url": "http://bellebethcooper.com/",
                    "location": "Melbourne, AU",
                    "avatar": "https://littlelogs.co/static/media/avatars/belle.png",
                    "timezone": "Australia/Melbourne"

                },

            "source": null,
            "content": "<p>Catching up on some fiddly <a class=\"tag\" href=\"/tag/admin\"><span class=\"hash\">#</span>admin</a> stuff this morning. Updated and backed up our <a class=\"tag\" href=\"/tag/HelloCode\"><span class=\"hash\">#</span>HelloCode</a> <a class=\"tag\" href=\"/tag/bookkeeping\"><span class=\"hash\">#</span>bookkeeping</a> docs, booked <a class=\"tag\" href=\"/tag/theatre\"><span class=\"hash\">#</span>theatre</a> tickets for this weekend, wrote up our company <a class=\"tag\" href=\"/tag/report\"><span class=\"hash\">#</span>report</a> for October, replied to some <a class=\"tag\" href=\"/tag/customerdevelopment\"><span class=\"hash\">#</span>customerdevelopment</a> questions about remote working, paid rent, updated <a class=\"tag\" href=\"/tag/exist\"><span class=\"hash\">#</span>exist</a> resource pages, and requested a refund for a bad app I bought recently.</p>",
            "comments": [ ],
            "tags": [

                "admin",
                "bookkeeping",
                "customerdevelopment",
                "exist",
                "HelloCode",
                "report",
                "theatre"

            ],
            "mentions": [ ],
            "likes": [

                {
                    "user": "mattimck",
                    "created": "2015-11-02T01:47:33Z"
                }
            ]

        }
    ]
}

A list of posts by the current user and others followed by the current user.

Request

GET /api/1/timelines/home/

Activity timeline

{

    "count": 1495,
    "next": "https://littlelogs.co/api/1/timelines/activity/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "id": 6263,
            "created": "2015-11-02T00:23:51Z",
            "type": "comment_after",
            "content": "rpgdan commented after you on a log",
            "originator":

            {
                "id": 88,
                "username": "rpgdan",
                "first_name": "Dan",
                "last_name": "",
                "bio": "Game developer working in Hong Kong. My side project is a book about how to make a JRPG from scratch.",
                "url": "http://howtomakeanrpg.com/",
                "location": "Hong Kong",
                "avatar": "https://littlelogs.co/static/media/avatars/rpgdan1441062803.png",
                "timezone": "GMT"

            },
            "log": {

                "id": 2613,
                "created": "2015-11-01T23:46:55Z",
                "user": {
                    "id": 88,
                    "username": "rpgdan",
                    "first_name": "Dan",
                    "last_name": "",
                    "bio": "Game developer working in Hong Kong. My side project is a book about how to make a JRPG from scratch.",
                    "url": "http://howtomakeanrpg.com/",
                    "location": "Hong Kong",
                    "avatar": "https://littlelogs.co/static/media/avatars/rpgdan1441062803.png",
                    "timezone": "GMT"
                },
                "source": null,
                "content": "<p>113 lines edited this morning. </p>\n<p>In November, for the book, I'd like to:</p>\n<ul>\n<li>Get all outsourced art finished</li>\n<li>Finish a first edit pass through the Combat part of the book</li>\n</ul>\n<p>To do this I'm going to need to up my editing amount a little.</p>\n<p>October went quite well:</p>\n<ul>\n<li>Finished Part I of the book (probably the biggest part)</li>\n<li>Final art for Part II</li>\n<li>Grew the content on the site by one post a week</li>\n<li>Went from ~1k visitors to ~4k visitors to the site</li>\n<li>67 mailing list signups (there's no funnel from articles to book signup yet.)</li>\n</ul>\n<p>I didn't realise how big a project I was taking on when I started this. There's still quite a long way to go. Whatever happens I'm going to open up some access in January; purchasing early access or something similar.</p>",
                "comments": [
                    SNIP
                ],
                "tags": [],
                "mentions": [],
                "likes": [
                    SNIP
                ]

            },
            "comment": {
                "id": 2540,
                "created": "2015-11-02T00:23:51Z",
                "user": SNIP ,
                "content": "<p>Thanks guys :)</p>\n<p>I'll check out the book too!</p>",
                "source": null,
                "tags": [],
                "mentions": []
            }

        }
    ]
}

I’ve snipped out a lot here for brevity, because even one notification (activity item) contains a lot.

You can see that a notification contains a type, a plain-text content field describing the notification, the originator user object, and then the full related log and comment objects. There should be enough within to reassemble the original log without any other requests. Not all notification types will have an attached comment, but each should have a log.

Request

GET /api/1/timelines/activity/

Public timeline

See all public logs.

Users

Get the current user

{
    "id": 1,
    "username": "josh",
    "first_name": "Josh",
    "last_name": "Sharp",
    "bio": "Making this thing right now.",
    "url": "http://hellocode.co/",
    "location": "Melbourne, AU",
    "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
    "timezone": "Australia/Melbourne"
}

If you’re not quite sure who you are, this endpoint will return the currently authenticated user.

Request

GET /api/1/users/$self/

View a user

{
    "id": 120,
    "username": "belle",
    "first_name": "Belle Beth",
    "last_name": "Cooper",
    "bio": "Writer and coder of things",
    "url": "http://bellebethcooper.com",
    "location": "Melbourne",
    "avatar": "http://littlelogs.co/static/media/avatars/belle.png",
    "timezone": "UTC"
}

Request

GET /api/1/users/<username>/

List follows for a user

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "username": "josh",
            "first_name": "Josh",
            "last_name": "Sharp",
            "bio": "Making this thing right now.",
            "url": "http://hellocode.co/",
            "location": "Melbourne, AU",
            "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
            "timezone": "Australia/Melbourne"

        },
    ]
}

A list of other users who are followed by this user.

Request

GET /api/1/users/followed-by/<username>/

List all following a user

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {

            "id": 1,
            "username": "josh",
            "first_name": "Josh",
            "last_name": "Sharp",
            "bio": "Making this thing right now.",
            "url": "http://hellocode.co/",
            "location": "Melbourne, AU",
            "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
            "timezone": "Australia/Melbourne"

        }
    ]
}

A list of other users who follow this user.

Request

GET /api/1/users/following/<username>/

Follow a user

Request

POST /api/1/users/<username>/follow/

Response

Returns 201 CREATED if successful, but no content.

Unfollow a user

Request

DELETE /api/1/users/<username>/unfollow/

Response

Returns 204 NO CONTENT if successful, but no content.

Logs

List all public logs

{
    "count": 2402,
    "next": "https://littlelogs.co/api/1/logs/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "id": 2617,
            "created": "2015-11-02T05:39:38Z",
            "user": {
                "id": 1,
                "username": "josh",
                "first_name": "Josh",
                "last_name": "Sharp",
                "bio": "Making this thing right now.",
                "url": "http://hellocode.co/",
                "location": "Melbourne, AU",
                "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
                "timezone": "Australia/Melbourne"
            },
            "source": null,
            "content": "<p>Whew! Spent ages collecting our <a class=\"tag\" href=\"/tag/exist\"><span class=\"hash\">#</span>exist</a> <a href=\"https://exist.io/blog/weekly-links-102-light/\">links post</a> for this week. We're changing tack and making each one a collection of articles and apps/devices based on a theme, which will culminate in a post <a class=\"content-user\" href=\"/belle\">@belle</a> writes that fits in with the recent themes. This one is about light — sunlight, too much light at night, etc., and tools around that. <a class=\"tag\" href=\"/tag/weeklylinks\"><span class=\"hash\">#</span>weeklylinks</a></p>",
            "comments": [],
            "tags": [
                "exist",
                "weeklylinks"
            ],
            "mentions":[
                "belle"
            ],
            "likes": []

        }

    ]
}

This is the same as the public timeline.

Request

GET /api/1/logs/

List logs for a tag

{

    "count": 12,
    "next": "https://littlelogs.co/api/1/logs/tag/blogging/?limit=20&offset=20",
    "previous": null,
    "results": [

        {
            "id": 2588,
            "created": "2015-10-29T04:28:49Z",
            "user": {
                "id": 1,
                "username": "josh",
                "first_name": "Josh",
                "last_name": "Sharp",
                "bio": "Making this thing right now.",
                "url": "http://hellocode.co/",
                "location": "Melbourne, AU",
                "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
                "timezone": "Australia/Melbourne"

            },
            "source": null,
            "content": "<p>Finally posted my thoughts from my earlier ReadWrite interview onto the Hello Code blog. Barely any of what I wrote ended up in the final piece (which is always the way) so I figured I'd repurpose it as a blog post rather than letting all those words go to waste. It's on <a href=\"http://blog.hellocode.co/post/future-activity-data/\">the future of activity tracking data</a>. <a class=\"tag\" href=\"/tag/blogging\"><span class=\"hash\">#</span>blogging</a></p>",
            "comments": [ ],
            "tags": [
                "blogging"
            ],
            "mentions": [ ],
            "likes": [
                {

                    "user": "kiriappeee",
                    "created": "2015-10-29T04:51:11Z"

                },
                {

                    "user": "belle",
                    "created": "2015-10-29T08:31:38Z"

                }
            ]

        }
    ]
}

Request

GET /api/1/logs/tag/<tag>/

List logs by a user

Request

{

    "count": 65,
    "next": "https://littlelogs.co/api/1/logs/user/kateo/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "id": 2606,
            "created": "2015-11-01T04:38:41Z",
            "user": {

                "id": 45,
                "username": "kateo",
                "first_name": "Kate ",
                "last_name": "",
                "bio": "",
                "url": "",
                "location": "Melbourne",
                "avatar": "https://littlelogs.co/static/media/avatars/kateo1430996134.png",
                "timezone": "Australia/Melbourne"

            },
            "source": null,
            "content": "<p>I completed all of the user login system lessons but it doesn't work properly. </p>\n<ul>\n<li>After logging in it displays the login page instead of the members page</li>\n<li>Logout isn't displayed after logging in </li>\n<li>Register is shown to logged in users</li>\n</ul>",
            "comments": [
                {
                    "id": 2521,
                    "created": "2015-11-01T04:49:59Z",
                    "user": {
                        "id": 45,
                        "username": "kateo",
                        "first_name": "Kate ",
                        "last_name": "",
                        "bio": "",
                        "url": "",
                        "location": "Melbourne",
                        "avatar": "https://littlelogs.co/static/media/avatars/kateo1430996134.png",
                        "timezone": "Australia/Melbourne"
                    },
                    "content": "<p>all fixed, that was quick. </p>\n<p>Notes to self:\n- spell correctly, flase and false are not the same\n- getUserbyID must be getUserById</p>",
                    "source": null,
                    "tags": [ ],
                    "mentions": [ ]
                }
            ],
            "tags": [ ],
            "mentions": [ ],
            "likes": [ ]
        }
    ]
}

GET /api/1/logs/user/<username>/

View a log

{
    "id": 2588,
    "created": "2015-10-29T04:28:49Z",
    "user": {
        "id": 1,
        "username": "josh",
        "first_name": "Josh",
        "last_name": "Sharp",
        "bio": "Making this thing right now.",
        "url": "http://hellocode.co/",
        "location": "Melbourne, AU",
        "avatar": "https://littlelogs.co/static/media/avatars/josh.png",
        "timezone": "Australia/Melbourne"

    },
    "source": null,
    "content": "<p>Finally posted my thoughts from my earlier ReadWrite interview onto the Hello Code blog. Barely any of what I wrote ended up in the final piece (which is always the way) so I figured I'd repurpose it as a blog post rather than letting all those words go to waste. It's on <a href=\"http://blog.hellocode.co/post/future-activity-data/\">the future of activity tracking data</a>. <a class=\"tag\" href=\"/tag/blogging\"><span class=\"hash\">#</span>blogging</a></p>",
    "comments": [ ],
    "tags": [
        "blogging"
    ],
    "mentions": [ ],
    "likes": [
        {

            "user": "kiriappeee",
            "created": "2015-10-29T04:51:11Z"

        },
        {

            "user": "belle",
            "created": "2015-10-29T08:31:38Z"

        }
    ]

}

Request

GET /api/1/logs/<id>/

Create a log

Request

POST /api/1/logs/create/

Parameters

Name Description
content Markdown content of the log

Response

Returns 201 CREATED if successful, and the new log.

Preview log content

Request

POST /api/1/logs/preview/

Parameters

Name Description
content Markdown content of the log

Response

Returns 200 OK if successful, and the parsed HTML content of the log.

Delete a log

Request

DELETE /api/1/logs/<id>/delete/

Response

Returns 204 NO CONTENT if successful, but no content.

Comments

Create a comment

Request

POST /api/1/logs/<id>/comment/

Parameters

Name Description
content Markdown content of the log

Response

Returns 201 CREATED if successful, and the updated parent log.

Delete a comment

Request

DELETE /api/1/comment/<id>/delete/

Response

Returns 204 NO CONTENT if successful, but no content.

Likes

Like a log

Request

POST /api/1/logs/<id>/like/

Response

Returns 201 CREATED if successful, and the updated log.

Unlike a log

Request

DELETE /api/1/logs/<id>/unlike/

Response

Returns 204 NO CONTENT if successful, but no content.

Tags

List all tags

{

    "count": 1136,
    "next": "https://littlelogs.co/api/1/tags/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "name": "52filmsbywomen",
            "created": "2015-10-17T13:17:37Z"

        },
        {
            "name": "7dayproject",
            "created": "2015-05-14T11:57:17Z"

        },
        {
            "name": "87transit",
            "created": "2015-10-21T05:01:27Z"

        },
        {
            "name": "accent",
            "created": "2015-03-01T22:47:26Z"

        },
        {
            "name": "accessibility",
            "created": "2015-02-28T01:24:41Z"

        },
        {
            "name": "accord",
            "created": "2015-09-10T16:24:25Z"

        },
        {
            "name": "accountant",
            "created": "2015-06-26T00:12:45Z"

        }
    ]
}

Request

GET /api/1/tags/

List tags for a user

{
    "count": 116,
    "next": "https://littlelogs.co/api/1/tags/user/josh/?limit=20&offset=20",
    "previous": null,
    "results": [
        {
            "name": "acting",
            "created": "2015-02-07T08:18:25Z"

        },
        {
            "name": "admin",
            "created": "2015-02-03T23:31:51Z"

        },
        {
            "name": "android",
            "created": "2015-02-04T11:40:06Z"

        },
        {
            "name": "ansible",
            "created": "2015-02-26T06:11:45Z"

        },
        {
            "name": "API",
            "created": "2015-04-18T12:59:04Z"

        },
        {
            "name": "blogging",
            "created": "2015-06-27T03:01:59Z"

        }
    ]
}

GET /api/1/tags/user/<username>/

[
    {

        "name": "makerbase",
        "created": "2015-10-14T19:52:48Z"

    },
    {

        "name": "littlelogs",
        "created": "2015-02-03T06:49:32Z"

    },
    {

        "name": "exist",
        "created": "2015-02-03T07:40:34Z"

    },
    {

        "name": "API",
        "created": "2015-04-18T12:59:04Z"

    },
    {

        "name": "outduo",
        "created": "2015-10-09T09:22:39Z"

    },
    {

        "name": "content",
        "created": "2015-02-03T07:40:27Z"

    },
    {

        "name": "replylater",
        "created": "2015-10-17T04:19:43Z"

    },
    {

        "name": "writing",
        "created": "2015-02-15T14:54:07Z"

    },
    {

        "name": "Ghost",
        "created": "2015-07-14T08:25:21Z"

    },

    {
        "name": "curo",
        "created": "2015-02-25T21:30:45Z"
    }

]

Request

GET /api/1/tags/trending/