MnmlRdr API Documentation (v1.5.2)

MnmlRdr is a feed reading service with a simple and easy-to-use REST API.

Table of Contents

Change Log


Making Requests

All MnmlRdr API requests occur over SSL (HTTPS). All endpoints require Basic Authentication, using the user’s email as the username and the user’s password as the password. A user’s email and password can also be exchanged for authentication tokens which are used in the same way.

The base URL for all MnmlRdr API requests is: https://mnmlrdr.com/api/

Example Request:

curl -u 'user@domain.com:password' https://mnmlrdr.com/api/feeds  

JSON

All API responses will be returned in JSON format by default.

These responses will include the Content-Type: application/json header.

JSONP

Responses can be returned in JSONP format for GET endpoints by setting callback=functionName in the query string:

curl -u 'user@domain.com:password' https://mnmlrdr.com/api/feeds?callback=fun  

fun({  
    "success":true,  
    "feeds":[ ... ]  
})  

JSONP responses will include the Content-Type: text/javascript header.

MessagePack

Responses can also be returned in binary MessagePack format for efficient transmission and deserialization.

To use, provide the application/x-msgpack MIME type in the Accept header of the request:

curl -i 'user@domain.com:password' -H 'Accept: application/x-msgpack' https://mnmlrdr.com/api/feeds  

MessagePack responses will include the Content-Type: application/x-msgpack header.


Authentication Tokens

A user’s email and password can be exchanged for independent API authentication tokens. This allows API access without having to store the user’s sensitive email and password information.

TokenObject:

TokenObject Example:

{
    "token": "028476039e6d49989c3832015e100e19",
    "secret": "ab1f6622a2764740b1ba158be7a27ee9",
    "encoded": "MDI4NDc2MDM5ZTZkNDk5ODljMzgzMjAxNWUxMDBlMTk6YWIxZjY2MjJhMjc2NDc0MGIxYmExNThiZTdhMjdlZTk="
}  

Create Authentication Token [POST /auth]

Exchange a user’s email and password for an authentication token. The user’s email and password must be provided using Basic authentication. See example.

Response 200 (application/json)

`TokenObject`  

Response 401 (application/json)

If invalid authentication is provided.

{
    "status":401,
    "success":false,
    "message":"Unauthorized"
}  

This authentication token flow is best used when making API requests on behalf of a user (i.e. an API Client):

  1. User provides their email and password to the Client application.

  2. Client application posts the email and password (using Basic authentication) to the /auth endpoint.

    curl -X POST -u 'user@domain.com:password' https://mnmlrdr.com/api/auth  
    
  3. If the user’s authentication is invalid, the API will respond with a 401 Unauthorized. The client application will message the user about the invalid credentials.

    {
        "status":401,
        "success":false,
        "message":"Unauthorized"
    }   
    
  4. If the user’s authentication is valid, the API will respond with a 200 OK, which will include a TokenObject in the response.

    {
        "token": "028476039e6d49989c3832015e100e19",
        "secret": "ab1f6622a2764740b1ba158be7a27ee9",
        "encoded": "MDI4NDc2MDM5ZTZkNDk5ODljMzgzMjAxNWUxMDBlMTk6YWIxZjY2MjJhMjc2NDc0MGIxYmExNThiZTdhMjdlZTk="
    }  
    
  5. Client application will store the TokenObject and use token and secret or encoded on subsequent API requests.

    curl  -u '028476039e6d49989c3832015e100e19:ab1f6622a2764740b1ba158be7a27ee9' https://mnmlrdr.com/api/feeds
    
    -or-
    
    curl -H "Authorization: Basic MDI4NDc2MDM5ZTZkNDk5ODljMzgzMjAxNWUxMDBlMTk6YWIxZjY2MjJhMjc2NDc0MGIxYmExNThiZTdhMjdlZTk=" https://mnmlrdr.com/api/feeds  
    

Feeds/Subscriptions

Feeds can be added to a user account individually, or through OPML import. The subscriptions list can also be exported to OPML.

FeedObject:

FeedObject Example:

{
    "id": 1314,
    "disabled": false,
    "display_title": "Widefido 123",
    "fetched": 1379004322,
    "hidden": false,
    "labels": [ 1, 2, 3 ],
    "missing": null,
    "site": "http://widefido.com/",
    "title": "Widefido",
    "url": "http://widefido.com/feed/",
    "user_title": "Widefido 123"
}  

Get Subscriptions [GET /feeds]

Get a list of subscriptions to which the user is currently subscribed

Parameters

Response 200 (application/json)

{
    "success":true,
    "feeds":[`FeedObject`, ... ]  
}  

Add Subscription [POST /feeds]

Add a subscription to the user account.

Parameters (form-data)

Response 200 (application/json)

`FeedObject`  

Response 400 (application/json)

If no URL is provided.

{
    "status":400,
    "success":false,
    "message":"Feed URL required"
}  

Response 400 (application/json)

If an invalid label is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid label"
}  

Get Subscription [GET /feed/{id}]

Get a specific subscription to which the user is currently subscribed.

Response 200 (application/json)

`FeedObject`  

Response 404 (application/json)

If a user is not subscribed to a particular feed, a 404 will be returned.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Update Subscription [PUT /feed/{id}]

Update a subscription’s title, label, and hidden state.

Parameters

Response 200 (application/json)

`FeedObject`  

Response 404 (application/json)

If the feed does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Response 400 (application/json)

If an invalid label is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid label"
}  

Delete Subscription [DELETE /feed/{id}]

Unsubscribe (delete) a subscription.

Response 200 (application/json)

{
    "success":true
}  

Response 404 (application/json)

If the feed does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Import Subscriptions OPML [POST /opml]

Import an OPML subscriptions list file.

Parameters (form-multipart)

Response 200 (application/json)

{
    "success":true,
    "feeds_imported":[ `FeedObject`, ... ]
}  

Response 400 (application/json)

If no OPML file is provided.

{
    "status":400,
    "success":false,
    "message":"OPML file required"
}  

Export Subscriptions OPML [GET /opml]

Export the feeds as an OPML subscriptions list

Response 200 (application/xml)

<?xml version="1.0" encoding="UTF-8" ?>
<opml version="1.0">...</opml>  

Labels/Folders

Feeds can be grouped by placing them in Labels. Labels are represented in the MnmlRdr user interface as folders, but since a feed can appear in multiple labels, they can also be considered as tags/tag groups.

LabelObject:

LabelObject Example:

{
    "id": 1,
    "display_label": "Important",
    "label": "Important",
    "feeds": [ 1, 2, 3 ]
}  

Get Labels [GET /labels]

Get a list of labels.

Parameters

Response 200 (application/json)

{
    "success":true,
    "labels":[ `LabelObject`, ... ]
}  

Add Label [POST /labels]

Add a new label.

Parameters (form-data)

Response 200 (application/json)

`LabelObject`  

Response 400 (application/json)

If no label is provided.

{
    "status":400,
    "success":false,
    "message":"Label required"
}  

Get Label [GET /label/{id}]

Get a label.

Parameters

Response 200 (application/json)

`LabelObject`  

Response 404 (application/json)

If label does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Update Label [PUT /label/{id}]

Update a label

Parameters (form-data)

Response 200 (application/json)

`LabelObject`  

Response 400 (application/json)

If no label is provided.

{
    "status":400,
    "success":false,
    "message":"Label required"
}  

Response 404 (application/json)

If label does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Delete Label [DELETE /label/{id}]

Delete a label. This only deletes the label reference. Feeds within this label will move to the root of the subscriptions list (essentially, “unlabeled”).

Response 200 (application/json)

{
    "success":true
}  

Response 404 (application/json)

If label does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Tags

Entries can be marked with custom meta-data, called tags. Tags can be used to filter entries and can displayed to the user similar to feeds and labels.

TagObject:

TagObject Example:

{
    "id": 1,
    "name": "Important",
    "tag": "important",
    "hidden": false,
    "count": 120
}  

Get Tags [GET /tags]

Get a list of tags.

Parameters

Response 200 (application/json)

{
    "success":true,
    "tags":[ `TagObject`, ... ]
}  

Add Tag [POST /tags]

Add a new tag.

Parameters (form-data)

Response 200 (application/json)

`TagObject`  

Response 400 (application/json)

If no name is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid tag name"
}  

Get Tag [GET /tags/{id}]

Get a tag.

Parameters

Response 200 (application/json)

`TagObject`  

Response 404 (application/json)

If tag does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Update Tag [PUT /tags/{id}]

Update a tag.

Parameters (form-data)

Response 200 (application/json)

`TagObject`  

Response 400 (application/json)

If no name is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid tag name"
}  

Response 404 (application/json)

If tag does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Delete Tag [DELETE /tags/{id}]

Delete a tag.

Response 200 (application/json)

{
    "success":true
}  

Response 404 (application/json)

If tag does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Entries

Entries can be retrieved and filtered by unread, by starred, by queued (read later), or by advanced filtering.

Entries maintain starred, queued, and unread metadata. These can be updated for each entry. Convenience methods exist for marking entries as read in bulk.

EntryObject:

EntryObject Example:

{
    "id": 6611,
    "author": null,
    "body": "<div>Turpis pede, aliquet ac, mattis sed, consequat in, massa. Com sociis natoque penatibus et magni.</div>",
    "created": 1366729675,
    "date": 1366732800,
    "date_formatted": "Apr 23",
    "date_iso": "2013-04-23T16:00:00",
    "date_relative": "about 5 months ago",
    "entry_created": null,
    "entry_published": null,
    "entry_updated": 1366732800,
    "feed_id": 3,
    "guid": "bbb4f4fabe634ba78b6d575f137c57fa",
    "modified": 1368363024,
    "permalink": "/entry/1",
    "readable": "/readable/691723a8ef76c753dee64365/8f13691723a8ef76c7",
    "source": "Some blog on the internet",
    "source_permalink": "/feed/3",
    "source_url": "http://www.domain.com/",
    "summary": "Turpis pede, aliquet ac, mattis sed, consequat in, massa. Com sociis...",
    "tags": [
        {
            "id":1,
            "name":"Important",
            "tag":"important",
            "hidden":false
        }
    ],
    "title": "Lorem ipsum is real",
    "url": "http://www.domain.com/entries/page.html",
    "is_starred": false,
    "is_queued": false,
    "is_unread": false,
    "is_tagged": true
}  

Entry Lists and Pagination

The entry lists will be returned as EntryListObject. These will be a paginated list of entries as defined by query parameters. returned in the methods below will be paginated. This pagination is based on a max_entry_id, an offset, and a limit.

EntryListObject:

EntryListObject Example:

{
    "query": {
        "compose_entry": true,
        "unread_only": true,
        "offset": 0,
        "limit": 20
    },
    "total": 2,
    "entries": [ `EntryObject`, ... ]
}  

Get Entries [GET /entries]

Query for a list of entries. A wide array of filtering parameters are available.

Parameters

Parameters for issuing an entry list query is defined by three parts: filters, pagination, and modifiers

Filters
Pagination
Modifiers

Response 200 (application/json)

`EntryListObject`  

Response 400 (application/json)

If invalid feed filter is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid feed"
}  

Response 400 (application/json)

If invalid label filter is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid label"
}  

Response 400 (application/json)

If invalid tag filter is provided.

{
    "status":400,
    "success":false,
    "message":"Invalid tag"
}  

Entries Query Examples

Get Unread Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?unread_only=true  

Get Unread Entries in Feed

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?unread_only=true&feed_id=1  

Get Starred Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?starred_only=true  

Get Queued Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?queued_only=true  

Get Tagged Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?tag_id=1  

Get Second Page of Anchored Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?max_entry_id=100&offset=0&limit=20  

Get Third Page of Anchored Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?max_entry_id=100&offset=20&limit=20  

Get Bulk Entries

curl  -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?entry_ids=1,2,3,4,5,6,7  

Update Entries [PUT /entries]

Update entry metadata (read/unread, starred/unstarred, queued/unqueued, tags) in bulk. This API mirrors the Get Entries API and accepts similar filter parameters.

Parameters

Filters (querystring)
Metadata (form-data)
Parameter Notes

When updating entries in bulk, it is best to provide a list of entry_ids on which to perform the modifications.

Cases where entry_ids are required:

Entry IDs are not required for marking all/labels/feeds as read (read=true)

Response 200 (application/json)

{
    "success":true
}  

Entries Bulk Update Examples

Mark All as Read

curl -X PUT -d 'read=true' -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries  

Mark Feed as Read

curl -X PUT -d 'read=true' -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?feed_id=1  

Mark as Starred

curl -X PUT -d 'starred=true' -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?entry_ids=1,2,3,4,5  

Mark as Unqueued

curl -X PUT -d 'queued=false' -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?entry_ids=5  

Mark as Tagged

curl -X PUT -d 'tags=important' -u 'user@domain.com:mnmlrdrapipassword' https://mnmlrdr.com/api/entries?entry_ids=1,2  

Get Entry [GET /entries/{id}]

Get a particular entry.

Parameters

Response 200 (application/json)

`EntryObject`  

Response 404 (application/json)

If entry does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Update Entry Metadata [PUT /entries/{id}]

Update entry metadata (read/unread, starred/unstarred, queued/unqueued)

Parameters

Response 200 (application/json)

`EntryObject`  

Response 404 (application/json)

If entry does not exist.

{
    "status":404,
    "success":false,
    "message":"Not found"
}  

Syncing with Deltas

The data housed within the MnmlRdr API is “the truth”, i.e., the most up-to-date version of user data lives within MnmlRdr.

To be more efficient, a client application may keep a local cache of user data and then sync changes from the API to keep the local cache up-to-date.

For efficient retrieval, developers can issue requests to retrieve the “deltas” that occured since the last sync. These deltas can then be used to update the local cache with the user data that has been modified.

NOTE: It is the responsibility of the client to propagate changes made locally by the user to the API. For example, when a user marks an Entry as “read”, a client application should promptly post that event to the API. If the client application cannot access the API at that time, it may store the action to be replayed to the API at a later time.

DeltaListObject:

DeltaListObject Example:

{
    "reset": true,
    "has_more": false,
    "deltas":[ `DeltaObject`, `DeltaObject`, ... ],
    "cursor":"a49KpHc"
}  

DeltaObject:

DeltaObject Example:

{
    "type":"entry",
    "id":123,
    "meta":{
        "unread":false, 
        "queued":true
    }
}

Get Deltas [GET /deltas]

Get the deltas (sync instructions) describing how to update a local cache to match the server’s state.

Parameters

Response 200 [no cursor provided] (application/json)

{
    "success": true,
    "reset": true,
    "has_more": false,
    "deltas":[
        {
            "type":"tag", 
            "id":1,
            "meta":{
                "refresh":{
                    "name":"Updated Tag",
                    "tag":"updated tag",
                    "hidden":false
                }
            }
        },
        {
            "type": "feed",
            "id":1,
            "meta": { "deleted":true }
        },  
        {
            "type": "feed",
            "id":2,
            "meta": { "unlabeled":[ 2 ] }
        },
        {
            "type": "entry",
            "id": 430,
            "meta": { "refresh":true }
        },
        {
            "type": "entry",
            "id": 432,
            "meta": { "starred": true, "queued": true }
        },
        {
            "type": "entry",
            "id": 431,
            "meta": { "unread": false, "tagged":[ 1 ] }
        }
    ],
    "cursor":"a49KpHc"
}  

Response 200 [cursor provided, no deltas to process] (application/json)

{
    "success": true,
    "reset": false,
    "has_more": false,
    "deltas": [],
    "cursor": "a49Kp3L"
}  

Example Sync Flow (first sync)

  1. Authenticate the user credentials and exchange for a token/secret.

  2. Perform an initial deltas request without a cursor:

    curl -u 'token:secret' https://mnmlrdr.com/api/deltas
    
    {
        "reset": true,
        "has_more": true,
        "deltas": [ 
            ...
        ],
        "cursor":"a49KqFt"
    }  
    
  3. When has_more=true, call /deltas repeatedly with each new cursor until has_more=false. Process the delta objects accordingly.

    curl -u 'token:secret' https://mnmlrdr.com/api/deltas?cursor=a49KqFt
    
    {
        "reset": false,
        "has_more": true,
        "deltas": [ 
            ...
        ],
        "cursor":"a71KqJt"
    }
    
    curl -u 'token:secret' https://mnmlrdr.com/api/deltas?cursor=a71KqJt
    
    {
        "reset": false,
        "has_more": false,
        "deltas": [ 
            ...
        ],
        "cursor":"a4CKkFx"
    }  
    
  4. Store the last returned cursor to be used on the next call to /deltas.

  5. Process each delta object by updating the local cache with the meta details provided. If the delta requests a refresh or the corresponding object isn’t in the local cache, place these in a queue to be requested and updated in bulk.


Example Sync Flow (in pseudocode)

    do while result.has_more:
        result = call /deltas api (with stored cursor if exists)

        for each delta in result.deltas:
            if delta.meta.refresh is an object:
                either:
                    update local cache with (delta.type, delta.id, delta.meta)
                or:
                    queue fresh value to be fetched
            else if delta.meta.refresh is true:
                queue fresh value to be fetched
            else if delta.meta.deleted is true:
                remove (delta.type, delta.id) from local cache
            else if (delta.type, delta.id) not in cache:
                queue fresh value to be fetched
            else:
                update local cache with (delta.type, delta.id, delta.meta)

        store result.cursor for the next call to /deltas


    for each batch of deltas of a given type in the fetch queue:
        fetch fresh values from api
        update local cache with fresh values from fetch