Reader API

The Reader API just supports saving new documents to Reader and fetching your documents. We will add more endpoints in the near future. If you have any questions, please reach out :)
Looking for the API docs for the original Readwise? See here.

Authentication

Set a header with key "Authorization" and value: "Token XXX" where XXX is your Readwise access token. You (or your users) can get that from here: readwise.io/access_token

If you want to check that a token is valid, just make a GET request to https://readwise.io/api/v2/auth/ with the above header. You should receive a 204 response.

Document CREATE

Request: POST to https://readwise.io/api/v3/save/

Parameters: A JSON object with the following keys:

Key Type Description Required
url string The document's unique URL. If you don't have one, you can provide a made up value such as https://yourapp.com#document1 yes
html string The document's content, in valid html (see examples). If you don't provide this, we will try to scrape the URL you provided to fetch html from the open web. no
should_clean_html boolean Only valid when html is provided. Pass true to have us automatically clean the html and parse the metadata (title/author) of the document for you. By default, this option is false. no
title string The document's title, it will overwrite the original title of the document no
author string The document's author, it will overwrite the original author (if found during the parsing step) no
summary string Summary of the document no
published_date date A datetime representing when the document was published in the ISO 8601 format; default timezone is UTC.
Example: "2020-07-14T20:11:24+00:00"
no
image_url string An image URL to use as cover image no
location string One of: new, later, archive or feed. Default is new.
Represents the initial location of the document (previously called triage_status). Note: if you try to use a location the user doesn't have enabled in their settings, this value will be set to their default location.
no
category string One of: article, email, rss, highlight, note, pdf, epub, tweet or video. Default is guessed based on the URL, usually article. no
saved_using string This value represents the source of the document no
tags list A list of strings containing tags, example: ["tag1", "tag2"] no
notes string A top-level note of the document no

Response:

  • Status code: 201 or 200 if document already exist
  • Created document details:


{
    "id": "0000ffff2222eeee3333dddd4444",
    "url": "https://read.readwise.io/new/read/0000ffff2222eeee3333dddd4444",
}
              

Usage/Examples:

  • JavaScript
  • 
    $.ajax({
      url: 'https://readwise.io/api/v3/save/',
      type: 'POST',
      contentType: 'application/json',
      beforeSend: function (xhr) {
        xhr.setRequestHeader('Authorization', 'Token XXX');
        },
      data: JSON.stringify({
        "url": "https://example.com/article/",
        "html": "<div><h1>This article is awesome</h1><p>content here!</p></div>"
        "tags": ["tag1", "tag2"]
      }),
      success: function (result) {console.log(result)},
      error: function (error) {console.log(error)},
    });
              
  • Python
  • 
    import requests
    requests.post(
        url="https://readwise.io/api/v3/save/",
        headers={"Authorization": "Token XXX"},
        json={
            "url": "https://example.com/article/",
            # No html is provided, so the url will be scraped to get the document's content.
            "tags": ["tag3", "tag4"]
        }
    )
                        
  • Bash
  • 
    $ curl -v https://readwise.io/api/v3/save/ -H "Authorization: Token XXX" -X POST -d '{"url": "https://example.com/article/"}' -H "Content-Type: application/json"
                        

Document LIST

Request: GET to https://readwise.io/api/v3/list/

Parameters: Usual query params:

Key Type Description Required
id string The document's unique id. Using this parameter it will return just one document, if found. no
updatedAfter string (formatted as ISO 8601 date) Fetch only documents updated after this date no
location string The document's location, could be one of: new, later, shortlist, archive, feed no
category string The document's category, could be one of: article, email, rss, highlight, note, pdf, epub, tweet, video no
pageCursor string A string returned by a previous request to this endpoint. Use it to get the next page of documents if there are too many for one request. no

Response:

  • Status code: 200
  • Please keep in mind that both highlights and notes made in Reader are also considered Documents. Highlights and notes will have `parent_id` set, which is the Document id of the article/book/etc and highlight that they belong to, respectively.
  • List of documents:


{
    "count": 2304,
    "nextPageCursor": "01gm6kjzabcd609yepjrmcgz8a",
    "results": [
        {
            "id": "01gwfvp9pyaabcdgmx14f6ha0",
            "url": "https://readiwise.io/feed/read/01gwfvp9pyaabcdgmx14f6ha0",
            "source_url": "https://www.driverlesscrocodile.com/values/ends-and-meanings-3-alasdair-macintyre-virtue-mortality-and-story-in-heroic-societies/",
            "title": "Ends and Meanings (3): Alasdair MacIntyre virtue, mortality and story in heroic societies",
            "author": "Stuart Patience",
            "source": "Reader RSS",
            "category": "rss",
            "location": "feed",
            "tags": {},
            "site_name": "Driverless Crocodile",
            "word_count": 819,
            "created_at": "2023-03-26T21:02:51.618751+00:00",
            "updated_at": "2023-03-26T21:02:55.453827+00:00",
            "notes": "",
            "published_date": "2023-03-22",
            "summary": "Without … a place in the social order, ...",
            "image_url": "https://i0.wp.com/www.driverlesscrocodile.com/wp-content/uploads/2019/10/cropped-driverlesscrocodile-icon-e1571123201159-4.jpg?fit=32%2C32&ssl=1",
            "parent_id": null,
            "reading_progress": 0.15,
        },
        {
            "id": "01gkqtdz9xabcd5gt96khreyb",
            "url": "https://readiwise.io/new/read/01gkqtdz9xabcd5gt96khreyb",
            "source_url": "https://www.vanityfair.com/hollywood/2017/08/the-story-of-the-ducktales-theme-music",
            "title": "The Story of the DuckTales Theme, History’s Catchiest Single Minute of Music",
            "author": "Darryn King",
            "source": "Reader add from import URL",
            "category": "article",
            "location": "new",
            "tags": {},
            "site_name": "Vanity Fair",
            "word_count": 2678,
            "created_at": "2022-12-08T02:53:29.639650+00:00",
            "updated_at": "2022-12-13T20:37:42.544298+00:00",
            "published_date": "2017-08-09",
            "notes": "A sample note",
            "summary": "A woo-hoo heard around the world.",
            "image_url": "https://media.vanityfair.com/photos/598b1452f7f0a433bd4d149c/16:9/w_1280,c_limit/t-ducktales-woohoo-song.png",
            "parent_id": null,
            "reading_progress": 0.5,
        },
        {
            "id": "01gkqt8nbms4t698abcdvcswvf",
            "url": "https://readwise.io/new/read/01gkqt8nbms4t698abcdvcswvf",
            "source_url": "https://www.vanityfair.com/news/2022/10/covid-origins-investigation-wuhan-lab",
            "title": "COVID-19 Origins: Investigating a “Complex and Grave Situation” Inside a Wuhan Lab",
            "author": "Condé Nast",
            "source": "Reader add from import URL",
            "category": "article",
            "location": "new",
            "tags": {},
            "site_name": "Vanity Fair",
            "word_count": 9601,
            "created_at": "2022-12-08T02:50:35.662027+00:00",
            "updated_at": "2023-03-22T13:29:41.827456+00:00",
            "published_date": "2022-10-28",
            "notes": "",
            "summary": "The Wuhan Institute of Virology, the cutting-edge ...",
            "image_url": "https://media.vanityfair.com/photos/63599642578d980751943b65/16:9/w_1280,c_limit/vf-1022-covid-trackers-site-story.jpg",
            "parent_id": null,
            "reading_progress": 0,
        }
    ]
}
              

Usage/Examples:

  • JavaScript
  • 
    const token = "XXX"; // use your access token here
    
    const fetchDocumentListApi = async (updatedAfter=null, location=null) => {
        let fullData = [];
        let nextPageCursor = null;
    
        while (true) {
          const queryParams = new URLSearchParams();
          if (nextPageCursor) {
            queryParams.append('pageCursor', nextPageCursor);
          }
          if (updatedAfter) {
            queryParams.append('updatedAfter', updatedAfter);
          }
          if (location) {
            queryParams.append('location', location);
          }
          console.log('Making export api request with params ' + queryParams.toString());
          const response = await fetch('https://readwise.io/api/v3/list/?' + queryParams.toString(), {
            method: 'GET',
            headers: {
              Authorization: `Token ${token}`,
            },
          });
          const responseJson = await response.json();
          fullData.push(...responseJson['results']);
          nextPageCursor = responseJson['nextPageCursor'];
          if (!nextPageCursor) {
            break;
          }
        }
        return fullData;
    };
    
    // Get all of a user's documents from all time
    const allData = await fetchDocumentListApi();
    
    // Get all of a user's archived documents
    const archivedData = await fetchDocumentListApi(location='archive');
    
    // Later, if you want to get new documents updated after some date, do this:
    const docsAfterDate = new Date(Date.now() - 24 * 60 * 60 * 1000);  // use your own stored date
    const newData = await fetchDocumentListApi(docsAfterDate.toISOString());
              
  • Python
  • 
    import datetime
    import requests  # This may need to be installed from pip
    
    token = 'XXX'
    
    def fetch_reader_document_list_api(updated_after=None, location=None):
        full_data = []
        next_page_cursor = None
        while True:
            params = {}
            if next_page_cursor:
                params['pageCursor'] = next_page_cursor
            if updated_after:
                params['updatedAfter'] = updated_after
            if location:
                params['location'] = location
            print("Making export api request with params " + str(params) + "...")
            response = requests.get(
                url="https://readwise.io/api/v3/list/",
                params=params,
                headers={"Authorization": f"Token {token}"}, verify=False
            )
            full_data.extend(response.json()['results'])
            next_page_cursor = response.json().get('nextPageCursor')
            if not next_page_cursor:
                break
        return full_data
    
    # Get all of a user's documents from all time
    all_data = fetch_reader_document_list_api()
    
    # Get all of a user's archived documents
    archived_data = fetch_reader_document_list_api(location='archive')
    
    # Later, if you want to get new documents updated after some date, do this:
    docs_after_date = datetime.datetime.now() - datetime.timedelta(days=1)  # use your own stored date
    new_data = fetch_reader_document_list_api(docs_after_date.isoformat())
                        
  • Bash
  • 
    $ curl -v https://readwise.io/api/v3/list/?location=later -H "Authorization: Token XXX" -H "Content-Type: application/json"
                        

Rate Limiting

The default base rate is 20 requests per minute. You can check Retry-After header in the 429 response to get the number of seconds to wait for.