The official Python client for the Instapaper API v2. Save articles, keep a user's library in sync, organize bookmarks into folders and tags, work with highlights, and read parsed article text.
It has no dependencies outside the Python standard library.
pip install instapaper-api
Requires Python 3.10 or later.
If you're calling the API for your own Instapaper account, such as from a script or a personal tool, you don't need the OAuth flow. Register an application, then generate an access token on its page. See Accessing your own account.
from instapaper import Instapaper client = Instapaper("your-access-token") for bookmark in client.bookmarks.list(): print(bookmark.title, bookmark.url) bookmark = client.bookmarks.save("https://example.com/article", title="An Article") client.bookmarks.like(bookmark.id)
To act on behalf of other Instapaper users, send them through the OAuth 2 authorization code flow. Add your redirect URI to your application's callback URIs first; it has to match exactly.
import secrets from instapaper import Instapaper, OAuth oauth = OAuth( client_id="your-client-id", client_secret="your-client-secret", redirect_uri="https://yourapp.example.com/callback", ) state = secrets.token_urlsafe(16) url = oauth.authorization_url(state=state) # Redirect the user to `url`. Instapaper sends them back to your redirect URI # with ?code=...&state=... once they approve. # In your callback, check that `state` matches, then: token = oauth.exchange_code(code) client = Instapaper(token.access_token)
Access tokens don't expire, so store the token rather than the code. Keep your client secret on your server.
# One page of a section: home (default), archive, liked, folder, or tag page = client.bookmarks.list(section="archive", limit=50) print(f"{len(page)} of {page.total}") for bookmark in page: print(bookmark.title) # Every bookmark in a folder, fetched page by page for bookmark in client.bookmarks.iterate(folder_id=99): ... # Save with tags, straight into a folder client.bookmarks.save("https://example.com/recipe", tags=["Recipes"], folder_id=99) # Save content that has no public URL client.bookmarks.save(private_source="Acme Reader", content="<p>...</p>", title="A Private Article") client.bookmarks.update(12345, progress=0.42) client.bookmarks.archive(12345) client.bookmarks.move_to_folder(12345, 99) client.bookmarks.update_tags(12345, add=["Recipes"], remove=[7]) client.bookmarks.delete(12345) # permanent, not the same as archiving
sync returns everything that changed since a Unix timestamp, across every section, including the IDs of bookmarks deleted since then.
import time started = int(time.time()) changes = client.bookmarks.sync(since=last_sync) for bookmark in changes.bookmarks: ... # insert or update your local copy for bookmark_id in changes.deleted_ids: ... # remove it locally last_sync = started
Use changes instead if you want to fetch one page at a time.
article = client.bookmarks.parse(12345) print(article.metadata.title) print(article.content.body) # HTML with scripts stripped
Parsing without a key is allowed for personal use, when the account you're reading belongs to the developer who registered the application. To read other users' articles, pass your own Instaparser key as instaparser_api_key. See Non-personal use, and read the API Terms of Use for what you may do with article text.
folder = client.folders.create("Recipes") client.folders.reorder({folder.id: 1}) client.folders.delete(folder.id) # its bookmarks move back to the home list tag = client.tags.create("Cooking") client.tags.rename(tag.id, "Food") highlight = client.highlights.create(12345, "The passage the reader marked.") client.highlights.list(12345) client.highlights.delete(highlight.id)
Accounts without Instapaper Premium can create five highlights a month. Past that, create raises PermissionDeniedError.
Every error this library raises is an InstapaperError. When the API answers with an error status you get an APIError subclass carrying status and message:
| Exception | Status | When |
|---|---|---|
BadRequestError |
400 | A missing or invalid argument |
AuthenticationError |
401 | The access token is missing, unknown, or revoked |
QuotaExceededError |
402 | Instaparser credits ran out |
PermissionDeniedError |
403 | The application isn't approved or is suspended, or a Premium limit |
NotFoundError |
404 | No such endpoint |
RateLimitError |
429 | Too many requests |
ServerError |
5xx | Something went wrong on Instapaper's side |
InstapaperConnectionError means the request never got a response, and OAuthError comes from OAuth.exchange_code.
from instapaper import AuthenticationError, RateLimitError try: client.bookmarks.save("https://example.com/article") except AuthenticationError: ... # ask the user to reconnect except RateLimitError: ... # back off and try again later
Branch on the exception type or status, not on message, which may be reworded over time.
The full API reference lives at instapaper.com/developers.
MIT. See LICENSE.