How to Create a Notion Page Using Python

With Notion’s API, we can now hook up other applications to Notion—including Python scripts. However, it requires a bit of setting up.

So here’s a guide on how you can create a Notion page in a database using Python! (Or you can scroll down to the end to just copy the script.)

Setting Up the Notion API

To use the Notion API, we first need to get an API token. Notion calls it an “Internal Integration Secret”.

  1. Go to Notion’s integrations page.
  2. Click on “New integration”.
  3. Fill in the “Basic Information” form.
  4. Copy the Internal Integration Secret.

You now have the Internal Integration Secret of your new integration, which you can paste into your Python script later.

But this integration will not have access to your whole workspace. To give it access to a database:

  1. Open your Notion database in the Notion app or browser.
  2. Click on the three dots on the top right.
  3. Click on “Add connections”.
  4. Click on your integration and click “Confirm”.

You can now access this database via the Notion API.

Checking the Database’s Properties

If we want to add page properties to the pages we create, we need to find out what properties the database has. First, make sure that the database has at least one page.

Here is the first Python script we are going to use, which will check the database’s properties:

import requests

NOTION_TOKEN = "XXX"  # Find your token here: https://www.notion.so/my-integrations
DATABASE_ID = "XXX"  # Database ID for your database, like "e7bd26c59e084e0bbaab0045939e7a81" in https://www.notion.so/sarahmakmq-tutorials/e7bd26c59e084e0bbaab0045939e7a81?v=716517a8f10848268ab7a0f8c572429f

headers = {
    "Authorization": "Bearer " + NOTION_TOKEN,
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28",  # Check what is the latest version here: https://developers.notion.com/reference/changes-by-version
}


def get_pages(num_pages=None):
    url = f"https://api.notion.com/v1/databases/{DATABASE_ID}/query"

    get_all = num_pages is None
    page_size = 100 if get_all else num_pages

    payload = {"page_size": page_size}
    response = requests.post(url, json=payload, headers=headers)

    data = response.json()

    results = data["results"]
    while data["has_more"] and get_all:
        payload = {"page_size": page_size, "start_cursor": data["next_cursor"]}
        url = f"https://api.notion.com/v1/databases/{DATABASE_ID}/query"
        response = requests.post(url, json=payload, headers=headers)
        data = response.json()
        results.extend(data["results"])

    return results


pages = get_pages()

for page in pages:
    page_id = page["id"]
    props = page["properties"]
    print(props)
    exit()
  1. Replace XXX in NOTION_TOKEN with your integration’s Internal Integration Secret.

  2. Replace XXX in DATABASE_ID with your Notion database ID.

    You can find the database ID in the URL to your database. For example, in the URL https://www.notion.so/example/<long_hash_1>?v=<long_hash_2>, the database ID is <long_hash_1>.

    Let’s use this database as an example. It has the database URL https://www.notion.so/sarahmakmq-tutorials/e7bd26c59e084e0bbaab0045939e7a81?v=716517a8f10848268ab7a0f8c572429f, so the database ID is e7bd26c59e084e0bbaab0045939e7a81.

  3. Run the script. It should print out something like this:

    {'Type': {'id': '~J%60%3E', 'type': 'select', 'select': {'id': '91313987-a4c0-4109-8a7b-99c65733f4ab', 'name': 'Electric', 'color': 'yellow'}}, 'Name': {'id': 'title', 'type': 'title', 'title': [{'type': 'text', 'text': {'content': 'Pikachu', 'link': None}, 'annotations': {'bold': False, 'italic': False, 'strikethrough': False, 'underline': False, 'code': False, 'color': 'default'}, 'plain_text': 'Pikachu', 'href': None}]}}
    
  4. It looks messy, so let’s make it more readable. You can format it in your preferred code editor, or on js-beautify online. Here is the beautified text:

    {
      "Type": {
        "id": "~J%60%3E",
        "type": "select",
        "select": {
          "id": "91313987-a4c0-4109-8a7b-99c65733f4ab",
          "name": "Electric",
          "color": "yellow"
        }
      },
      "Name": {
        "id": "title",
        "type": "title",
        "title": [
          {
            "type": "text",
            "text": {
              "content": "Pikachu",
              "link": null
            },
            "annotations": {
              "bold": false,
              "italic": false,
              "strikethrough": false,
              "underline": false,
              "code": false,
              "color": "default"
            },
            "plain_text": "Pikachu",
            "href": null
          }
        ]
      }
    }
    

We can see that there is a title property and a select property, and we know what options we can fill when we add these properties.

So now we can add these properties to our new page. (We can omit any of these properties too, but those property fields would be blank.)

Adding a Page

This is the next script we will use, which will add a new page to the database, and add blocks inside the page.

import requests

NOTION_TOKEN = "XXX"  # Find your token here: https://www.notion.so/my-integrations
DATABASE_ID = "XXX"  # Database ID for your database, like "e7bd26c59e084e0bbaab0045939e7a81" in https://www.notion.so/sarahmakmq-tutorials/e7bd26c59e084e0bbaab0045939e7a81?v=716517a8f10848268ab7a0f8c572429f

headers = {
    "Authorization": "Bearer " + NOTION_TOKEN,
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28",  # Check what is the latest version here: https://developers.notion.com/reference/changes-by-version
}


def create_page(data: dict):
    create_url = "https://api.notion.com/v1/pages"

    payload = {"parent": {"database_id": DATABASE_ID}, "properties": data}

    res = requests.post(create_url, headers=headers, json=payload)
    if res.status_code == 200:
        print(f"{res.status_code}: Page created successfully")
    else:
        print(f"{res.status_code}: Error during page creation")
    return res


properties = XXX

response = create_page(properties)

page_block_id = response.json()["id"]


def edit_page(page_block_id, data: dict):
    edit_url = f"https://api.notion.com/v1/blocks/{page_block_id}/children"

    payload = data

    res = requests.patch(edit_url, headers=headers, json=payload)
    if res.status_code == 200:
        print(f"{res.status_code}: Page edited successfully")
    else:
        print(f"{res.status_code}: Error during page editing")
    return res


blocks = {"children": []}

edit_page(page_block_id, blocks)

We will fill up the properties and blocks variables in the next sections.

To start:

  1. Change the NOTION_TOKEN and DATABASE_ID to your desired values.

  2. Replace XXX in properties with the printed output of your first script.

    In our example, this was the output of the first script:

    {
      "Type": {
        "id": "~J%60%3E",
        "type": "select",
        "select": {
          "id": "91313987-a4c0-4109-8a7b-99c65733f4ab",
          "name": "Electric",
          "color": "yellow"
        }
      },
      "Name": {
        "id": "title",
        "type": "title",
        "title": [
          {
            "type": "text",
            "text": {
              "content": "Pikachu",
              "link": null
            },
            "annotations": {
              "bold": false,
              "italic": false,
              "strikethrough": false,
              "underline": false,
              "code": false,
              "color": "default"
            },
            "plain_text": "Pikachu",
            "href": null
          }
        ]
      }
    }
    

    So the properties value will look like this:

    properties = {
     "Type": {
         "id": "~J%60%3E",
         "type": "select",
         "select": {
             "id": "91313987-a4c0-4109-8a7b-99c65733f4ab",
             "name": "Electric",
             "color": "yellow",
         },
     },
     "Name": {
         "id": "title",
         "type": "title",
         "title": [
             {
                 "type": "text",
                 "text": {"content": "Pikachu", "link": None},
                 "annotations": {
                     "bold": False,
                     "italic": False,
                     "strikethrough": False,
                     "underline": False,
                     "code": False,
                     "color": "default",
                 },
                 "plain_text": "Pikachu",
                 "href": None,
             }
         ],
     },
    }
    
  3. Assign variables to the fields of the properties you want to adjust.

    The properties above are the same as an existing page in your database, but we will probably want to change the properties for each page.

    Let’s start with editing the text field in the title property—so we can change the title of the page.

    To edit the title with a Python variable, replace the string in "content" and "plain_text" with the variable.

    "text": {"content": "Pikachu", "link": None}
    

    Let’s call this variable title.

    "text": {"content": title, "link": None}
    
  4. Next, assign a value to the variable the top of the script.

    For our example, we’ll assign the string "Bulbasaur" our title variable:

    title = "Bulbasaur"
    
  5. Edit the fields of each property, and add Python variables for the fields you want to adjust. This is the list of database properties.

    If your property has the id and color fields (such as multi_select, select, and status), remove these two fields.

     "Type": {
         "id": "~J%60%3E",
         "type": "select",
         "select": {
             "id": "91313987-a4c0-4109-8a7b-99c65733f4ab",
             "name": "Electric",
             "color": "yellow",
         },
     }
    
    "Type": {
        "id": "~J%60%3E",
        "type": "select",
        "select": {
            "name": "Electric",
        },
    }
    

    Note that some properties cannot be edited in the API, so you need to remove them from the properties variable. Currently, these properties are rollup, created_by, created_time, last_edited_by, and last_edited_time. See Notion’s API wiki for the updated list of properties that cannot be edited.

Adding Blocks to the Page

So far, the script allows us to add a page (with its properties) to the database. To add blocks to the page, we need to adjust the blocks variable in the script.

blocks = {"children": []}

To add blocks to the page, we need to add the fields each blocks needs, and fill up the blocks variable accordingly. See this page to learn about each block type and the fields they need.

If we want to add a text block, we can add the text block’s fields like so:

blocks = {
    "children": [
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": text_content,
                        },
                    }
                ]
            },
        },
    ]
}

Then we need to assign a value to the variable text_content earlier in the script.

text_content = "Bulbasaur is a small, quadrupedal amphibian Pokémon that has blue-green skin with darker patches."

However, there is a potential issue—Notion’s API has character limits (which you can learn about here), so to be safe, we need to handle this somehow, like splitting text into chunks that are under 2000 characters.

chunks = re.findall(r".{1,2000}(?=\s|$)", text_content)

blocks = {
    "children": [
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": chunk.strip(),
                        },
                    }
                ]
            },
        }
        for chunk in chunks
    ]
}

The Final Script

For our example, this is what the final script could look like:

import requests
import re

NOTION_TOKEN = "XXX"  # Find your token here: https://www.notion.so/my-integrations
DATABASE_ID = "XXX"  # Database ID for your database

title = "Bulbasaur"
text_content = "Bulbasaur is a small, quadrupedal amphibian Pokémon that has blue-green skin with darker patches."
type_text = "Grass"

headers = {
    "Authorization": "Bearer " + NOTION_TOKEN,
    "Content-Type": "application/json",
    "Notion-Version": "2022-06-28",  # Check the latest version here: https://developers.notion.com/reference/changes-by-version
}


def create_page(data: dict):
    create_url = "https://api.notion.com/v1/pages"
    payload = {"parent": {"database_id": DATABASE_ID}, "properties": data}
    res = requests.post(create_url, headers=headers, json=payload)
    if res.status_code == 200:
        print(f"{res.status_code}: Page created successfully")
    else:
        print(f"{res.status_code}: Error during page creation")
    return res


properties = {
    "Type": {
        "id": "~J%60%3E",
        "type": "select",
        "select": {
            "name": type_text,
        },
    },
    "Name": {
        "id": "title",
        "type": "title",
        "title": [
            {
                "type": "text",
                "text": {"content": title, "link": None},
                "annotations": {
                    "bold": False,
                    "italic": False,
                    "strikethrough": False,
                    "underline": False,
                    "code": False,
                    "color": "default",
                },
                "plain_text": title,
                "href": None,
            }
        ],
    },
}

response = create_page(properties)
page_block_id = response.json()["id"]


def edit_page(page_block_id, data: dict):
    edit_url = f"https://api.notion.com/v1/blocks/{page_block_id}/children"
    res = requests.patch(edit_url, headers=headers, json=data)
    if res.status_code == 200:
        print(f"{res.status_code}: Page edited successfully")
    else:
        print(f"{res.status_code}: Error during page editing")
    return res


chunks = re.findall(r".{1,2000}(?=\s|$)", text_content)

blocks = {
    "children": [
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
                "rich_text": [
                    {
                        "type": "text",
                        "text": {
                            "content": chunk.strip(),
                        },
                    }
                ]
            },
        }
        for chunk in chunks
    ]
}

edit_page(page_block_id, blocks)

Hence, this script adds a page like this. It’s titled “Bulbasaur”, and its Type property is “Grass”. In the page, there is one text block with the text “Bulbasaur is a small, quadrupedal amphibian Pokémon that has blue-green skin with darker patches.” Perfect!

With a script like this, you can now use Python to easily add pages to Notion!

References

  1. https://www.python-engineer.com/posts/notion-api-python/