---
title: "Building a Notion PowerShell Module: Part 1"
description: "Explore the process of building a Notion PowerShell Module, enabling seamless integration and automation between the Notion platform and Windows PowerShell"
canonical: "https://adamtheautomator.com/notion-powershell/"
---

# Building a Notion PowerShell Module: Part 1

> Explore the process of building a Notion PowerShell Module, enabling seamless integration and automation between the Notion platform and Windows PowerShell

Source: https://adamtheautomator.com/notion-powershell/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Building a Notion PowerShell Module: Part 1](https://adamtheautomator.com/wp-content/uploads/2023/10/Notion-PowerShell.jpg)

# Building a Notion PowerShell Module: Part 1

[![](https://secure.gravatar.com/avatar/f08b754bc0dce1c76685f6afa93185ecfb6f861fd14f993286e06bba69eaeb8b?s=192&d=mm&r=g)Adam Listek](https://adamtheautomator.com/author/alistek/)18 October 20236 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[Notion](/tag/notion/)[PowerShell](/tag/powershell/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [Creating a Notion Integration Token](#creating-a-notion-integration-token)
*   [Interacting with the Notion API in PowerShell](#interacting-with-the-notion-api-in-powershell)
*   [Retrieving the Pages Blocks](#retrieving-the-pages-blocks)
*   [Creating an Advanced PowerShell Function](#creating-an-advanced-powershell-function)
*   [Bonus! Reduce Passed Parameters Through PSDefaultParameterValues](#bonus-reduce-passed-parameters-through-psdefaultparametervalues)
*   [Wrapping Up](#wrapping-up)

This tutorial will teach you how to create an advanced function to interact with the [Notion API](https://developers.notion.com/). [Notion](https://www.notion.so/) is a powerful web application to manage knowledge in flexible workspaces. Take your automation to the next level with Notion PowerShell integration!

## Prerequisites

To follow along in this tutorial, you only need a Notion account and [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3); here, PowerShell v7.3.7 is in use.

## Creating a Notion Integration Token

Once signed into Notion, open a browser to the [“My integrations” page](https://www.notion.so/my-integrations). Here, you will create a new integration. The resulting secret key will be used in every request, authenticating your REST API calls.

1\. Once on the **My Integrations** page, click the “**Create new integration**” placeholder or “**\+ New integration**” button.

![Creating a new integration.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-106.png)

Creating a new integration.

2\. Next, fill the details out accordingly. Choose the **Associated Workspace** and give a **Name** to identify the integration. Optionally, upload a **Logo** to differentiate your integration further.

![Entering the necessary integration details.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-105.png)

Entering the necessary integration details.

3\. Click the eye icon to show the integration secret. Copy this integration secret for later use in the [PowserShell script](https://adamtheautomator.com/powershell-7-upgrade/).

![Retrieving the integration secret.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-104.png)

Retrieving the integration secret.

4\. Navigate to the **Capabilities** section and make any necessary changes, here, the defaults are used.

![Modifying the integration capabilities.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-103.png)

Modifying the integration capabilities.

## Interacting with the Notion API in PowerShell

With the integration created and the secret key in hand, navigate to the given page you will interact with. You may give an integration access to the top-level, or root, of a series of pages. When granted access to the root of a series of pages, all child pages will also be accessible by the integration.

In this example, a PowerShell Testing page has been created. You can add the integrations access through the Connections menu by clicking on the three dots in the upper-right corner to access additional menu options. Highlight the “Add connections” and choose your newly created integration named “PowerShell.”

![Granting the integration access to a given page.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-107.png)

Granting the integration access to a given page.

A confirmation will show, and as noted, will ask if you grant the integration access to all child pages as well.

![Confirming the integration access.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-108.png)

Confirming the integration access.

### Retrieving the Pages Blocks

Everything in Notion is based on the idea of blocks. A paragraph is considered a block, even an empty one. Since this page has no content, you can request the content, and the default empty paragraph block will be returned.

You will need several pieces of information to create a REST API call to Notion.

*   **API Key** – The previously created integration and saved API key.
*   **API URI** – The [URL of the REST API](https://developers.notion.com/reference/intro#conventions).
*   **API Version** – Breaking changes can, and do, occur from time to time in the REST API. You need to give the API version you are operating against. The [current version](https://developers.notion.com/reference/changes-by-version) is `2022-06-28` at the time of this article’s creation.
*   **Page Size** – You can only [request so many blocks](https://developers.notion.com/reference/request-limits) in a single REST API call, and the maximum for the child blocks call is `100`.
*   **GUID (Globally Unique Identifier)** – Each Notion page is identified by its GUID, the value appended after friendly text. You can copy and paste that value from the URL. For example, the page is `PowerShell-Testing-a2b3646de9414df4874d56153139b618`, but the GUID is `a2b3646de9414df4874d56153139b618`.

> _💡 Although the secret key is shown, it has since been removed and is no longer valid._

With all those pieces in hand, it’s time to create your REST API call. Like most REST API calls in PowerShell, you will call the [`Invoke-RestMethod`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7.3) cmdlet. The parameters are laid out using parameter splatting for readability, and the request results are stored in the `$Result` variable. To see the results, the `results` property is requested at the end.

```powershell
$APIKey     = 'secret_fS2A21NwTL4L6XWFTTdgmF3xboWiYXExKZ8Pw15oRMw'
$APIURI     = 'https://api.notion.com/v1'
$APIVersion = '2022-06-28'
$PageSize   = '100'
$GUID       = 'a2b3646de9414df4874d56153139b618'

$Params = @{
    "Headers" = @{
        "Authorization"  = "Bearer {0}" -F $APIKey
        "Content-type"   = "application/json"
        "Notion-Version" = "{0}" -F $APIVersion
    }
    "Method"  = 'GET'
    "URI"     = ("{0}/blocks/{1}/children?page_size={2}" -F $APIURI, [GUID]::new($GUID), $PageSize)
}

$Result = Invoke-RestMethod @Params

$Result.results
```

![Showing the results of a REST API call to Notion.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-111.png)

Showing the results of a REST API call to Notion.

As you can see, a single block was returned with detailed information. The actual content, though blank, is within the `paragraph` property and the `rich_text` property. Right now, there is nothing contained within.

![](https://adamtheautomator.com/wp-content/uploads/2023/10/image-110.png)

If the text “Hello from Notion!” is entered in the test page, and the code is rerun, then the returned `rich_text` value will reflect that.

![Displaying the rich text of a Notion block.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-109.png)

Displaying the rich text of a Notion block.

## Creating an Advanced PowerShell Function

With the basics in hand, what are the next steps? To make a more robust reusable function, you can leverage the tenets of advanced PowerShell function. The improvements are less code to run on the command line, easier script use, and more validation. In addition, with the `page_size` limit of `100`, a page over that will need a recursive call to retrieve all of the results, which this advanced function can do.

With the intent in mind, how does the code work? For any advanced function, you must include the [`[CmdletBinding()]`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.3) underneath the function declaration. This indicates to PowerShell that you may use the advanced features.

Next, the `Param` code block is declared. For two parameters, `$APIURI`, and `$GUID`, advanced validation is done via the [`ValidateScript`](https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/validatescript-attribute-declaration?view=powershell-7.3) decorator. Using the [`[System.URI]::IsWellFormedUriString`](https://learn.microsoft.com/en-us/dotnet/api/system.uri.iswellformeduristring?view=net-7.0) method, this validates that the passed value is an accurate URI. The [`[GUID]::Parse`](https://learn.microsoft.com/en-us/dotnet/api/system.guid.parse?view=net-7.0) method on the [`[GUID]` type accelerator](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_type_accelerators?view=powershell-7.3) tests if the passed-in value is a true GUID.

In the Begin block, instead of redefining the parameters passed to `Invoke-RestMethod` on every iteration, they are defined once at the start of the pipeline. The tricker parts are in the `Process` block. The `start_cursor` parameter for Notion defines where the start of the result gathering begins. If defined, the previous values will be skipped, and the following `100` blocks will be counted from there.

In this function, that is only defined on a recursive call. For example, Notion will return a true or false `$Results.has_more` value if more than `100` blocks exist. If this is true, call the same `Get-NotionBlock` function, pass in the same parameters via the [`$PSBoundParameters` special variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.3#psboundparameters), and give the `$Result.next_cursor` value to the `-StartCursor` parameter. This will continue to do so until `has_more` is false.

Similarly, this is how the `has_children` property works, as child blocks may need to be retrieved as well. The function will call itself to retrieve those before continuing.

```powershell
Function Get-NotionBlock {
    [CmdletBinding()]

    Param(
        [String]$APIKey,
        [String]$APIVersion,
        [ValidateScript( { [System.URI]::IsWellFormedUriString( $_ ,[System.UriKind]::Absolute ) } )][String]$APIURI,
        [ValidateScript( { Try { If ( [GUID]::Parse( $_ ) ) { $True } } Catch { $False } } )][String]$GUID,
        [String]$StartCursor,
        [Int]$PageSize = 100
    )

    Begin {
        $Params = @{
            "Headers" = @{
                "Authorization"  = "Bearer {0}" -F $APIKey
                "Content-type"   = "application/json"
                "Notion-Version" = "{0}" -F $APIVersion
            }
            "Method"  = 'GET'
        }
    }

    Process {
        Try {
            If ($StartCursor) {
                $Params.Add("URI" , ("{0}/blocks/{1}/children?start_cursor={3}&page_size={2}" -F $APIURI, [GUID]::new($GUID), $PageSize, $StartCursor))
            } Else {
                $Params.Add("URI" , ("{0}/blocks/{1}/children?page_size={2}" -F $APIURI, [GUID]::new($GUID), $PageSize))
            }

            Write-Verbose "[Process] Params: $($Params | Out-String)"
            
            $Result = Invoke-RestMethod @Params

            If ($Result.has_more) {
                $Result.results

                If ([Bool]($Result.results | Where-Object has_children -EQ $True)) {
                    $Result.results | Where-Object has_children -EQ $True | ForEach-Object { Get-NotionBlock @PSBoundParameters -StartCursor:$Null -GUID $PSItem.id }
                }

                Write-Verbose "[Process] More Results Exist"

                Get-NotionBlock @PSBoundParameters -StartCursor $Result.next_cursor
            } Else {
                $Result.results

                If ([Bool]($Result.results | Where-Object has_children -EQ $True)) {
                    $Result.results | Where-Object has_children -EQ $True | ForEach-Object { Get-NotionBlock @PSBoundParameters -StartCursor:$Null -GUID $PSItem.id }
                }
            }
        } Catch {
            $Message = ($Error[0].ErrorDetails.Message | ConvertFrom-JSON).message

            Write-Error "Command Failed to Run: $Message"
        }
    }
}
```

With the advanced function defined, it’s time to call the function and retrieve the blocks! Create the `$Params` variable and pass that splatted variable into the `Get-NotionBlock` function. Here, only the first object is shown.

```powershell
$Params = @{
    "APIKey"     = 'secret_fS2A21NwTL4L6XWFTTdgmF3xboWiYXExKZ8Pw15oRMw'
    "APIVersion" = '2022-06-28'
    "APIURI"     = 'https://api.notion.com/v1'
    "GUID"       = 'a2b3646de9414df4874d56153139b618'
}

Get-NotionBlock @Params | Select-Object -First 1
```

![Showing the first block of the retrieved results.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-113.png)

Showing the first block of the retrieved results.

What if the page has over `100` blocks? When that happens, the function runs recursively. By passing in the `-Verbose` parameter, you can see how the `start_cursor` changes. To keep the results concise, pass to the `Measure-Object` cmdlet to show the over `158` results returned. You can tell by the verbose output that the `start_cursor` is defined in the second call and that more results exist.

![Displaying the results of a page with over 100 blocks.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-112.png)

Displaying the results of a page with over 100 blocks.

### Bonus! Reduce Passed Parameters Through PSDefaultParameterValues

You may have noticed that in the `$Params` block, you are passing the API Key, API Version, and API URI. As these three values very rarely change, it would be convenient not to have to do that every time. PowerShell has a mechanism to make this easier.

With the [`$PSDefaultParameterValues`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parameters_default_values?view=powershell-7.3) variable, you can define default parameter values once placed in your profile. Every time a function is called, these values will be given if no other value is defined at runtime.

```powershell
$PSDefaultParameterValues = @{
    "Get-NotionBlock:APIKey"     = 'secret_fS2A21NwTL4L6XWFTTdgmF3xboWiYXExKZ8Pw15oRMw'
    "Get-NotionBlock:APIVersion" = '2022-06-28'
    "Get-NotionBlock:APIURI"     = 'https://api.notion.com/v1'
}
```

Making the same call as before, but simply passing the `GUID` in means that you can use the function even easier!

![Utilizing the $PSDefaultParameterValues variable.](https://adamtheautomator.com/wp-content/uploads/2023/10/image-114.png)

Utilizing the `$PSDefaultParameterValues` variable.

## Wrapping Up

This is just the start of your Notion PowerShell journey! This article showcases a single REST API call but lays the groundwork for more possibilities to come. Integrate Notion into your PowerShell scripts and take advantage of both technologies’ flexibility and power.

The same principles in play for this article hold for implementing all of the other methods, which will be shown in future articles!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fnotion-powershell%2F&text=Building%20a%20Notion%20PowerShell%20Module%3A%20Part%201)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fnotion-powershell%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fnotion-powershell%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
