Readwise Reader API⚑
Source code in readwise/api.py
class ReadwiseReader:
def __init__(
self,
token: str,
):
'''
Readwise Reader API client.
Documentation for the Readwise Reader API can be found here:
https://readwise.io/reader_api
Args:
token: Readwise Reader Connector token
'''
self._token = token
self._url = 'https://readwise.io/api/v3'
@property
def _session(self) -> requests.Session:
'''
Session object for making requests.
The headers are set to include the token.
'''
session = requests.Session()
session.headers.update(
{
'Accept': 'application/json',
'Authorization': f'Token {self._token}',
}
)
return session
def _request(
self, method: str, endpoint: str, params: dict = {}, data: dict = {}
) -> requests.Response:
'''
Make a request to the Readwise Reader API.
The request is rate limited to 20 calls per minute.
Args:
method: HTTP method
endpoint: API endpoints
params: Query parameters
data: Request body
Returns:
requests.Response
'''
url = self._url + endpoint
logging.debug(f'Calling "{method}" on "{url}" with params: {params}')
response = self._session.request(method, url, params=params, json=data)
while response.status_code == 429:
seconds = int(response.headers['Retry-After'])
logging.warning(f'Rate limited by Readwise, retrying in {seconds} seconds')
sleep(seconds)
response = self._session.request(method, url, params=params, data=data)
response.raise_for_status()
return response
def get(self, endpoint: str, params: dict = {}) -> requests.Response:
'''
Make a GET request to the Readwise Reader API client.
Args:
endpoint: API endpoints
params: Query parameters
Returns:
requests.Response
'''
logging.debug(f'Getting "{endpoint}" with params: {params}')
return self._request('GET', endpoint, params=params)
def get_with_limit_20(self, endpoint: str, params: dict = {}) -> requests.Response:
'''
Get a response from the Readwise Reader API with a rate limit of 20 requests
per minute.
Args:
endpoint: API endpoint
params: Query parameters
Returns:
requests.Response
'''
return self.get(endpoint, params)
def _get_pagination(
self,
get_method: Literal['get', 'get_with_limit_20'],
endpoint: str,
params: dict = {},
) -> Generator[dict, None, None]:
'''
Get a response from the Readwise Reader API with pagination.
Args:
get_method: Method to use for making requests
endpoint: API endpoint
params: Query parameters
page_size: Number of items per page
Yields:
dict: Response data
'''
pageCursor = None
while True:
if pageCursor:
params.update({'pageCursor': pageCursor})
logging.debug(f'Getting page with cursor "{pageCursor}"')
try:
response = getattr(self, get_method)(endpoint, params=params)
except ChunkedEncodingError:
logging.error(f'Error getting page with cursor "{pageCursor}"')
sleep(5)
continue
data = response.json()
yield data
if (
type(data) == list
or not data.get('nextPageCursor')
or data.get('nextPageCursor') == pageCursor
):
break
pageCursor = data.get('nextPageCursor')
def get_pagination_limit_20(
self, endpoint: str, params: dict = {}
) -> Generator[dict, None, None]:
'''
Get a response from the Readwise Reader API with pagination and a rate limit
of 20 requests per minute.
Args:
endpoint: API endpoint
params: Query parameters
page_size: Number of items per page
Yields:
Response data
'''
yield from self._get_pagination('get_with_limit_20', endpoint, params)
def post(self, endpoint: str, data: dict = {}) -> requests.Response:
'''
Make a POST request to the Readwise Reader API.
Args:
endpoint: API endpoints
data: Request body
Returns:
requests.Response
'''
url = self._url + endpoint
logging.debug(f'Posting "{url}" with data: {data}')
response = self._request('POST', endpoint, data=data)
response.raise_for_status()
return response
def create_document(
self,
url: str,
html: str | None = None,
should_clean_html: bool | None = None,
title: str | None = None,
author: str | None = None,
summary: str | None = None,
published_at: datetime | None = None,
image_url: str | None = None,
location: Literal['new', 'later', 'archive', 'feed'] = 'new',
saved_using: str | None = None,
tags: list[str] = [],
) -> requests.Response:
'''
Create a document in Readwise Reader.
Args:
url: Document URL
html: Document HTML
should_clean_html: Whether to clean the HTML
title: Document title
author: Document author
summary: Document summary
published_at: Date and time the document was published
image_url: An image URL to use as cover image
location: Document location
saved_using: How the document was saved
tags: List of tags
Returns:
requests.Response
'''
data: dict[str, Any] = {
'url': url,
'tags': tags,
'location': location,
}
if html:
data['html'] = html
if should_clean_html is not None:
data['should_clean_html'] = should_clean_html
if title:
data['title'] = title
if author:
data['author'] = author
if summary:
data['summary'] = summary
if published_at:
data['published_at'] = published_at.isoformat()
if image_url:
data['image_url'] = image_url
if saved_using:
data['saved_using'] = saved_using
return self.post('/save/', data)
def get_documents(
self, params: dict = {}
) -> Generator[ReadwiseReaderDocument, None, None]:
for data in self.get_pagination_limit_20('/list/', params=params):
for document in data['results']:
yield ReadwiseReaderDocument(
id=document['id'],
url=document['url'],
source_url=document['source_url'],
title=document['title'],
author=document['author'],
source=document['source'],
category=document['category'],
location=document['location'],
tags=document['tags'],
site_name=document['site_name'],
word_count=document['word_count'],
created_at=datetime.fromisoformat(document['created_at']),
updated_at=datetime.fromisoformat(document['updated_at']),
notes=document['notes'],
published_date=document['published_date'],
summary=document['summary'],
image_url=document['image_url'],
parent_id=document['parent_id'],
reading_progress=document['reading_progress'],
)
__init__(token)
⚑
Readwise Reader API client.
Documentation for the Readwise Reader API can be found here: https://readwise.io/reader_api
Parameters:
Name | Type | Description | Default |
---|---|---|---|
token |
str
|
Readwise Reader Connector token |
required |
Source code in readwise/api.py
def __init__(
self,
token: str,
):
'''
Readwise Reader API client.
Documentation for the Readwise Reader API can be found here:
https://readwise.io/reader_api
Args:
token: Readwise Reader Connector token
'''
self._token = token
self._url = 'https://readwise.io/api/v3'
create_document(url, html=None, should_clean_html=None, title=None, author=None, summary=None, published_at=None, image_url=None, location='new', saved_using=None, tags=[])
⚑
Create a document in Readwise Reader.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
url |
str
|
Document URL |
required |
html |
str | None
|
Document HTML |
None
|
should_clean_html |
bool | None
|
Whether to clean the HTML |
None
|
title |
str | None
|
Document title |
None
|
author |
str | None
|
Document author |
None
|
summary |
str | None
|
Document summary |
None
|
published_at |
datetime | None
|
Date and time the document was published |
None
|
image_url |
str | None
|
An image URL to use as cover image |
None
|
location |
Literal['new', 'later', 'archive', 'feed']
|
Document location |
'new'
|
saved_using |
str | None
|
How the document was saved |
None
|
tags |
list[str]
|
List of tags |
[]
|
Returns:
Type | Description |
---|---|
Response
|
requests.Response |
Source code in readwise/api.py
def create_document(
self,
url: str,
html: str | None = None,
should_clean_html: bool | None = None,
title: str | None = None,
author: str | None = None,
summary: str | None = None,
published_at: datetime | None = None,
image_url: str | None = None,
location: Literal['new', 'later', 'archive', 'feed'] = 'new',
saved_using: str | None = None,
tags: list[str] = [],
) -> requests.Response:
'''
Create a document in Readwise Reader.
Args:
url: Document URL
html: Document HTML
should_clean_html: Whether to clean the HTML
title: Document title
author: Document author
summary: Document summary
published_at: Date and time the document was published
image_url: An image URL to use as cover image
location: Document location
saved_using: How the document was saved
tags: List of tags
Returns:
requests.Response
'''
data: dict[str, Any] = {
'url': url,
'tags': tags,
'location': location,
}
if html:
data['html'] = html
if should_clean_html is not None:
data['should_clean_html'] = should_clean_html
if title:
data['title'] = title
if author:
data['author'] = author
if summary:
data['summary'] = summary
if published_at:
data['published_at'] = published_at.isoformat()
if image_url:
data['image_url'] = image_url
if saved_using:
data['saved_using'] = saved_using
return self.post('/save/', data)
get(endpoint, params={})
⚑
Make a GET request to the Readwise Reader API client.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
endpoint |
str
|
API endpoints |
required |
params |
dict
|
Query parameters |
{}
|
Returns:
Type | Description |
---|---|
Response
|
requests.Response |
Source code in readwise/api.py
def get(self, endpoint: str, params: dict = {}) -> requests.Response:
'''
Make a GET request to the Readwise Reader API client.
Args:
endpoint: API endpoints
params: Query parameters
Returns:
requests.Response
'''
logging.debug(f'Getting "{endpoint}" with params: {params}')
return self._request('GET', endpoint, params=params)
get_documents(params={})
⚑
Source code in readwise/api.py
def get_documents(
self, params: dict = {}
) -> Generator[ReadwiseReaderDocument, None, None]:
for data in self.get_pagination_limit_20('/list/', params=params):
for document in data['results']:
yield ReadwiseReaderDocument(
id=document['id'],
url=document['url'],
source_url=document['source_url'],
title=document['title'],
author=document['author'],
source=document['source'],
category=document['category'],
location=document['location'],
tags=document['tags'],
site_name=document['site_name'],
word_count=document['word_count'],
created_at=datetime.fromisoformat(document['created_at']),
updated_at=datetime.fromisoformat(document['updated_at']),
notes=document['notes'],
published_date=document['published_date'],
summary=document['summary'],
image_url=document['image_url'],
parent_id=document['parent_id'],
reading_progress=document['reading_progress'],
)
get_pagination_limit_20(endpoint, params={})
⚑
Get a response from the Readwise Reader API with pagination and a rate limit of 20 requests per minute.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
endpoint |
str
|
API endpoint |
required |
params |
dict
|
Query parameters |
{}
|
page_size |
Number of items per page |
required |
Yields: Response data
Source code in readwise/api.py
def get_pagination_limit_20(
self, endpoint: str, params: dict = {}
) -> Generator[dict, None, None]:
'''
Get a response from the Readwise Reader API with pagination and a rate limit
of 20 requests per minute.
Args:
endpoint: API endpoint
params: Query parameters
page_size: Number of items per page
Yields:
Response data
'''
yield from self._get_pagination('get_with_limit_20', endpoint, params)
get_with_limit_20(endpoint, params={})
⚑
Get a response from the Readwise Reader API with a rate limit of 20 requests per minute.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
endpoint |
str
|
API endpoint |
required |
params |
dict
|
Query parameters |
{}
|
Returns: requests.Response
Source code in readwise/api.py
def get_with_limit_20(self, endpoint: str, params: dict = {}) -> requests.Response:
'''
Get a response from the Readwise Reader API with a rate limit of 20 requests
per minute.
Args:
endpoint: API endpoint
params: Query parameters
Returns:
requests.Response
'''
return self.get(endpoint, params)
post(endpoint, data={})
⚑
Make a POST request to the Readwise Reader API.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
endpoint |
str
|
API endpoints |
required |
data |
dict
|
Request body |
{}
|
Returns:
Type | Description |
---|---|
Response
|
requests.Response |
Source code in readwise/api.py
def post(self, endpoint: str, data: dict = {}) -> requests.Response:
'''
Make a POST request to the Readwise Reader API.
Args:
endpoint: API endpoints
data: Request body
Returns:
requests.Response
'''
url = self._url + endpoint
logging.debug(f'Posting "{url}" with data: {data}')
response = self._request('POST', endpoint, data=data)
response.raise_for_status()
return response