How to pull Notion site via PowerShell

By Robert Dyjas on  • Edit this post

The article demonstrates getting the site content from Notion using PowerShell. It uses free Notion API to pull all the blocks.

I currently explore using Notion as my notepad for writing blog posts. One of the steps I plan is to convert Notion pages to MDX. Before going there, I need to pull the site content. This article demonstrates the steps to do so.


For the demo we'll need:

  • account (free personal license is enough and will be used by me)
  • PowerShell (I use the newest preview version 7.3.0-preview.6)

Creating the database

We'll need a database, so let's create one. At the bottom of the Notion's left bar, hit New page:

Creating new page in Notion

Let's name it Test DB and create a database table:

Creating a database table

From here we'll create a new database, instead of connecting to the existing one. We'll click New database at the bottom right:

Creating a new database in the wizard

From here we'll see the database with 3 empty records:

The database with 3 empty records

Let's add some names to it:

The database with names

Now let's add some content. Click Open (1) next to the page name. Then hit Enter or click below the comment section (2):

Adding the content to the page

Add some content there and click outside the page view to save:

Saving the edits

Setting up the integration

We will set up the private integration and get the internal integration key. Follow the guide from Notion: Getting started: Step 1. Copy the key, we'll need it later.

For the capabilities, we only need the Read content. No user information is needed. For reference, see the picture below:

Integration capabilities

Now, make sure to share the database with the integration. You're done with the setup.

Getting the database entries

We can now start working on our script. First, let's set variables.

We'll need our integration key. We copied it in the previous step.

We'll also need our database ID. As mentioned on the Getting started page:

The database ID is the part of the URL after your workspace name (if you have one) and the slash (myworkspace/) and before the question mark (?). The ID is 32 characters long, containing numbers and letters.

                                  |--------- Database ID --------|

Copy the ID and paste it somewhere you can easily find later.

We're saving them to variables:

# We're storing the key as a secure string
# because... it's secure
$notionKey = 'secret_xxxxxx'| ConvertTo-SecureString -AsPlainText -Force
$notionDatabaseId = 'a8aec43384f447ed84390e8e42c2e089'

Request parameters

Now we gather all the parameters and save them to a hashtable.

To find out which parameters are required, we refer to the API reference for the database query.


Notion API reference can generate the PowerShell cmdlet for us. Choose PowerShell on the right pane and then change from Invoke-WebRequest to Invoke-RestMethod. We'll use the generated code but with some improvements.

The code generated from the API reference page is:

$headers.Add("Accept", "application/json")
$headers.Add("Notion-Version", "2022-06-28")
$headers.Add("Content-Type", "application/json")
$headers.Add("Authorization", "Bearer secret_xxxxxx")
$response = Invoke-RestMethod -Uri '' -Method POST -Headers $headers -ContentType 'application/json' -Body '{"page_size":100}'

First, let's remove the unnecessary parameters. According to Notion API reference for pagination, page_size defaults to 100, so we don't need it. Therefore, we don't need a request body.

We also have Content-Type added twice. Let's remove the header and use the cmdlet parameter instead.

We now have:

$headers.Add("Accept", "application/json")
$headers.Add("Notion-Version", "2022-06-28")
$headers.Add("Authorization", "Bearer secret_xxxxxx")
$response = Invoke-RestMethod -Uri '' -Method POST -Headers $headers -ContentType 'application/json'

Now, let's replace the hardcoded values (database ID and integration token) with our variables. Note that we'll change single quotes to double quotes. Double quotes allow passing the variables inline.

For the toke - we converted it to SecureString. We'd either need to decrypt it or use the cmdlet parameters Token and Authentication. That's the option we choose. Token accepts SecureString so we can pass it directly.

We now have:

$headers.Add("Accept", "application/json")
$headers.Add("Notion-Version", "2022-06-28")
$response = Invoke-RestMethod -Uri "$notionDatabaseId/query" -Method POST -Headers $headers -ContentType 'application/json' -Authentication 'Bearer' -Token $notionKey

Almost ready! Only two more changes. We'll now convert to splatting. We'll also change the name of the response variable:

$dbRequestParams = @{
	Uri = "$notionDatabaseId/query"
	Method = 'POST'
	Headers = $headers
	ContentType = 'application/json'
	Authentication = 'Bearer'
	Token = $notionKey
$dbRequestRes = Invoke-RestMethod @dbRequestParams


The response we should receive will look like that:

Database query response

It's quite clear that we're interested in the results property. Let's inspect it:

Objects in the results property

Voilà! We got the array of objects. All objects contain the page ID. We'll use it later.


Note that the page URL contains its ID at the end. Next time, we can get it from the URL instead of querying the database.

Getting the page block children

We now have the page object. However, it only contains the page property. It's mentioned in the API docs article: Page object.

The Page object contains the property values of a single Notion page. (…) Page content is available as blocks. The content can be read using retrieve block children.

Ok, so now we need to retrieve block children. We'll modify our previous request with the new URL, and another method. We'll also update the variable names. The rest of the parameters stay as it is:

$pageId = $dbRequestRes.results[0].id
$pageChildrenParams = @{
	Uri = "$pageId/children"
	Method = 'GET'
	Headers = $headers
	ContentType = 'application/json'
	Authentication = 'Bearer'
	Token = $notionKey
$pageChildrenRes = Invoke-RestMethod @pageChildrenParams

Similarly, let's inspect the results property:

Page block's children objects

We now got the array of blocks. Each block represents the block on the page.

Going through the blocks and converting them to a readable format is out of the scope of this article. We got the data and that's it for now.


Notion's API is beginner-friendly, especially when it comes to authorization. We went through sending two requests. We've also learned a bit about the object types from the API. Now the tough tasks - how to convert the objects to Markdown.

Do you want this article to have a part 2? Let me know in the comments!