Exploring the new Notion API

Photo of a Laptop running Notion by Filip Baotić on Unsplash

Notion is a novel note-taking application. Users can create pages that support third-party integrations and interactive elements as well as databases with different views.

They recently released the beta version of their developer API. I wanted to learn what can be done using that API and even build a handy web application that will be able to visualize all page references as a graph. If you are interested in that web app, follow me on Twitter.

Structure

In order to understand the API, first, one has to understand how the content in Notion is organized: The basic building blocks are pages and databases. Both can have multiple pages and databases as children. Pages can also contain so-called “blocks”, which can be of different types and contain the actual content (pages are blocks, too - but more on that later). Databases and pages are very similar but differ not only in how they are presented to the user (different database views vs sub-pages). Their data is also structured differently: Pages only have a title and metadata (like creation date, author) as properties. Databases define properties, which each (database-)child (aka. item/entry that is contained in that database) will then inherit (let’s call them “entries”) those properties. This allows them to be displayed in different views that filter and structure by property. The database pages can’t have children themselves. There is also a special page type: The workspace (which is the root node of the page-tree) only contains the title and (page/database) children.

Exemplary workspace structure

The API documentation can be found here. Notion provides a JavaScript library, which implements the full API in TypeScript.

Authentication

There are currently two ways to authenticate with the Notion API, which are documented here. Internal integrations are just for one workspace and are great for prototyping and personal tools. You register the application, assign a workspace where you have admin access level and retrieve the token, which you use for every request as bearer token:

GET /v1/pages/b55c9c91-384d-452b-81db-d1ef79372b75 HTTP/1.1
Authorization: Bearer {MY_NOTION_TOKEN}

To give other users access to your application, you have to implement proper OAuth 2.0 authentication. This works by giving your users an URL, which forwards them to Notion, where they agree to give your application certain permissions. After that, Notion will forward the user back to your API (or redirect_uri). For that, you compose a URL where you append your URL-escaped application’s client_id and redirect_uri s well as response_type=code, like this:

https://api.notion.com/v1/oauth/authorize?client_id=<my-client-id>&redirect_uri=http%3A%2F%2Flocalhost%2Fauth&response_type=code

Note: You can use this tool to encode your URL’s properly.

The redirect response will then contain a code, which can be exchanged using a POST request to /v1/oauth/token and your applications’ client id and secret for a token, as explained here. With that token, you then have access to the users’ notion pages.

Accessing the API

The rest of the API is documented here (not sure why the authentication part is missing), but there are still some gotchas I wanna point out. The whole API is versioned and requires the user to provide the targeted version number in the header: “Notion-Version”: “2021–08–16” .

To list all pages in the users’ workspace, you can use the /v1/search route without an actual query. The pagination implementation is horrible: For example, they won’t tell you how many entries (or pagination pages) your query results in. You only get a cursor, which you can use to request the next couple of results after your last request.

Each page is also a block. You can just use the page id in the block API. To get all blocks of a page, you send a GET request to /v1/blocks/<page_id>/children. The response will also contain the content of those blocks. But keep a reference to the parent page because the response won’t contain that.

Rate Limits

According to the documentation three requests per second are allowed. They also state that some bursts beyond that limit are allowed. From my experience, they are really generous with those. Tho they explicitly state that the rate limits will change in future. They recommend implementing queues to send requests as long as no HTTP 429 is received. But I think it would be more efficient to just send three requests every second and never encounter that error in the first place.

Conclusion

Overall the Notion API is designed very well. Upon further inspection, some rough edges become apparent. But please keep in mind that the API is still in Beta. Authentication is pretty much standard. I really like that the API is fully versioned (even though there should be a default version when not specifying the header). The object structure is confusing at first but makes sense (maybe it could be explained better) and keeps the API quite simple. However, O would really want a way to search for blocks and specific types of blocks (to directly get all mentions). Also, it would be nice if the search for pages could directly return the content of the page. The pagination really has to be improved.

If you want to know more about the Notion API, checkout the docs. You won’t regret diving into this API! Let me know about what you implemented to improve your Notion workflow!

M. Sc Informatics Student in Munich, Germany. Full-stack software engineer and solutions architect.