Explore API
Tags: #notion #productivity
Author: Jeremy Ravenel
This notebook is an exploration of what you can do with the Notion's API.
Resources:
  • Notion official documentation : https://developers.notion.com/
  • Youtube video (not official) : https://www.youtube.com/watch?v=sdn1HgxLwEg

Input

1
import requests
2
import pandas as pd
3
import json
Copied!

Setting up Notion connections

  1. 1.
    Create a page or a database on Notion.
  2. 2.
    Create an integration in your workspace.
  3. 3.
    Share your page or database to this integration.
More explanation on how to do this here: https://developers.notion.com/docs/getting-started
1
DATABASE_ID_TEST = "a296bd16b7284bc494aa91f50ad64d30" #https://www.notion.so/a296bd16b7284bc494aa91f50ad64d30?v=d37af84a3a6744fb957002073a267c44
2
3
PAGE_ID = "e2e8b31737174dbe86b9ae65f9b8eb9c" #click on Page and Get ID : https://www.notion.so/Mary-Meeks-2d822179eb59451e91e83086cdd74e5c
4
5
INTEGRATION_TOKEN = "secret_gF6bJPSyOgt5oZgb2sgT1yiMxfS4LqNmWmd2M8S5vzl"
Copied!
1
NOTION_DB_URL = "https://api.notion.com/v1/databases/"
2
3
NOTION_PAGE_URL = "https://api.notion.com/v1/pages/"
4
5
NOTION_PAGE_CONTENT = "https://api.notion.com/v1/blocks/"
Copied!

Model

Get database properties

More information here: https://developers.notion.com/reference/get-database
1
database_url = NOTION_DB_URL + DATABASE_ID_TEST
2
3
response = requests.get(database_url, headers={"Authorization": f"{INTEGRATION_TOKEN}"})
4
print (response.json())
Copied!
More information here: https://developers.notion.com/reference/post-database-query
1
database_url = NOTION_DB_URL + DATABASE_ID_TEST + "/query"
2
3
query = {"filter": {"property": "High Priority", "checkbox": {"equals": True}}}
4
query = {"filter": {"property": "Cost of next trip", "number": {"greater_than_or_equal_to": 0.5}}}
5
6
headers = {"Authorization": f"{INTEGRATION_TOKEN}", "Notion-Version": "2021-05-13"}
7
8
response = requests.post(database_url, headers=headers, data=query)
9
print((response.json()['results']))
Copied!
1
df_structure = pd.DataFrame.from_dict(response.json()['results'])
Copied!
1
print("The size of the df is", df_structure.shape)
2
df_structure.head()
Copied!
⚠️ Notion's API allows us to retrieve a maximum of 100 records. So if your base is bigger than 100 records it will only retrieve the 100 last edited ones.
⚠️ If your database has some relation to some other databases, think to share the linked databases with the integration aswell.
As we can see the content of the Notion table is in the properties column. We will now extract it and see what it contains.
The column properties contain a dictionary for each Notion record. We will exctract each of these disctionnaries and create a new dataFrame.
1
list_dict = []
2
for index, row in df_structure.iterrows():
3
list_dict.append(row['properties'])
4
5
temp_df = pd.DataFrame.from_dict(list_dict)
Copied!
1
# Get the columns name in a list to use them later
2
columns = temp_df.columns.values.tolist()
Copied!
1
temp_df.head()
Copied!
As we can see, each of the properties contains another dict of the information.
Let's see how the dictionnary containing the data is structured.
1
for index, value in temp_df.iloc[2].items():
2
print(value)
Copied!
1
pd.DataFrame.from_dict(list_dict).iloc[0]['Name']
Copied!
Let's create a small function to extract the data.
All the properties contain an id and a type. The type will then be used to find the original information of the property.
Sometimes, the data will be contained directly as a string, sometimes it will be a dict sometimes it will be a list of dict.

Query database

1
def extract_name_or_plaintext(dictionnary):
2
# Given a dictionnary it will output the string of the key name or plain_text
3
if 'name' in dictionnary.keys():
4
return dictionnary['name']
5
elif 'plain_text' in dictionnary.keys():
6
return dictionnary['plain_text']
7
else:
8
return ''
9
10
def extract_date(dictionnary):
11
# For the moment we extract only the starting date of a date field
12
# Example {'id': 'prop_1', 'type': 'date', 'date': {'start': '2018-03-21', 'end': None}}
13
# Input : {'start': '2018-03-21', 'end': None}
14
return dictionnary['start']
Copied!
1
def extract_data(element):
2
# input: a dictionnary of a notion property
3
# Exemple: {'id': 'W#4k', 'type': 'select', 'select': {'id': 'b305bd26-****-****-****-c78e2034db8f', 'name': 'Client', 'color': 'green'}}
4
# output: the string containing the information of the dict. (Client in the exemple)
5
if type(element) is dict:
6
dict_type = element['type']
7
informations = element[dict_type]
8
9
if dict_type == 'date':
10
informations = extract_date(informations)
11
12
elif type(informations) is dict:
13
informations = extract_name_or_plaintext(informations)
14
15
elif type(informations) is list:
16
informations_temp = ''
17
for element_of_informations_list in informations:
18
informations_temp += extract_name_or_plaintext(element_of_informations_list) + ", "
19
informations = informations_temp[:-2]
20
return informations
21
22
else:
23
return ''
Copied!
1
all_list = []
2
for i in range (temp_df.shape[0]):
3
temp_list = []
4
for index, value in temp_df.iloc[i].items():
5
temp_list.append(extract_data(value))
6
all_list.append(temp_list)
7
df_content = pd.DataFrame.from_records(all_list, columns = columns)
Copied!

Get only visible headers

1
df_content.head()
Copied!

Get full headers

1
df_full = pd.concat([df_structure, df_content], axis=1)
Copied!
1
df_full
Copied!

Get page properties

There is two different API calls to interact with a page.
  • Get a page will give us a page properties: https://developers.notion.com/reference/get-page
  • Get block children will give us the page content: https://developers.notion.com/reference/get-block-children
1
page_url = NOTION_PAGE_URL + PAGE_ID
2
3
response = requests.get(page_url, headers={"Authorization": f"{INTEGRATION_TOKEN}", "Notion-Version": "2021-05-13"})
4
print (response.json())
Copied!

Retrieve a page content

1
page_url = NOTION_PAGE_CONTENT + PAGE_ID + "/children"
2
headers = {"Authorization": f"{INTEGRATION_TOKEN}", "Notion-Version": "2021-05-13"}
3
4
response = requests.get(page_url, headers=headers)
5
print(response.json())
Copied!
Some types are accessible. But some other types are unsupported and cannot be read through the API.
Some unsupported types :
  • image
  • bookmarked link
  • other page (it has an unsupported type but it will be readable through its page id)

Create a record

1
page_url = NOTION_PAGE_URL
2
page_id = DATABASE_ID_TEST
3
4
# Surprisingly in this case you need to add "Content-Type": "application/json"
5
# If not you will an error 400: body failed validation: body.parent should be defined, instead was 'undefined'.
6
headers = {"Authorization": f"{INTEGRATION_TOKEN}", "Notion-Version": "2021-05-13", "Content-Type": "application/json"}
7
8
name = {"title":[{"text":{"content":"Added via API NAAS"}}]}
9
company = {"rich_text": [{"text": {"content": "Test Company"}}]}
10
status = {"select": {"name": "Lost"}}
11
est_value = {"number": 10000 }
12
13
header = {"object": "block",
14
"type": "heading_2",
15
"heading_2": {
16
"text": [{ "type": "text", "text": { "content": "Naas API test" } }]
17
}
18
}
19
20
paragraph = {"object": "block",
21
"type": "paragraph",
22
"paragraph": {
23
"text": [
24
{
25
"type": "text",
26
"text": {
27
"content": "Notebooks as a service for data geeks. Naas notebooks enable you to script faster with low-code formulas & templates. Automate all your tasks in minutes..",
28
}
29
}
30
]
31
}
32
}
33
34
to_do = {"object": "block",
35
"type": "to_do",
36
"to_do": {
37
"text": [
38
{
39
"type": "text",
40
"text": {
41
"content": "Automate all your tasks in minutes..",
42
}
43
},
44
{
45
"type": "text",
46
"text": {
47
"content": "Script faster",
48
}
49
}
50
]
51
}
52
}
Copied!

Output

Setup object to post

1
myobj = {
2
"parent": {"database_id": page_id},
3
"properties":
4
{
5
"Name":name,
6
"Company": company,
7
"Status": status,
8
"Estimated Value": est_value
9
},
10
"children":[header, paragraph,to_do]
11
}
Copied!

Post record

1
data = json.dumps(myobj)
2
3
response = requests.post(page_url, headers=headers, data=data)
4
5
if 'status' in response.json().keys():
6
if response.json()['status'] != 200:
7
print ("Error:", response.json()['message'])
8
elif 'object' in response.json().keys():
9
print("✅ Your data was added to Notion")
10
print(response.json())
Copied!
Copy link
Edit on GitHub