Notion API
It is needed once per few years and each time took some time to recover knowledge, so writing a note for myself
Let's pretend in Notion we have an movies database with some sample fields
We will use following script to get some sample movies
$genres = Invoke-RestMethod "https://api.themoviedb.org/3/genre/movie/list?language=en" -Headers @{ Authorization = "Bearer $($env:TMDB)" } | Select-Object -ExpandProperty genres
$movies = @()
$movies += Invoke-RestMethod "https://api.themoviedb.org/3/movie/popular?language=en-US&page=1" -Headers @{ Authorization = "Bearer $($env:TMDB)" } | Select-Object -ExpandProperty results | Select-Object title, overview, @{n = 'poster'; e = { "https://image.tmdb.org/t/p/w500$($_.poster_path)" } }, @{n = 'rating'; e = { $_.vote_average } }, @{n = 'year'; e = { [int]$_.release_date.Substring(0, 4) } }, @{n = 'genres'; e = { $_.genre_ids | ForEach-Object { $genres | Where-Object id -eq $_ | Select-Object -ExpandProperty name } } }
$movies += Invoke-RestMethod "https://api.themoviedb.org/3/movie/top_rated?language=en-US&page=1" -Headers @{ Authorization = "Bearer $($env:TMDB)" } | Select-Object -ExpandProperty results | Select-Object title, overview, @{n = 'poster'; e = { "https://image.tmdb.org/t/p/w500$($_.poster_path)" } }, @{n = 'rating'; e = { $_.vote_average } }, @{n = 'year'; e = { [int]$_.release_date.Substring(0, 4) } }, @{n = 'genres'; e = { $_.genre_ids | ForEach-Object { $genres | Where-Object id -eq $_ | Select-Object -ExpandProperty name } } }
$movies | ConvertTo-Json | Out-File movies.json
Next step is to configure and database in Notion with corresponding columts
After that is done we gonna need to
https://www.notion.so/profile/integrations
Create integration, and grab its token
Then, in notion, click on tripple dots, and choose created integration from connections menu
After that following sample scripts should work
Invoke-RestMethod -Method Post "https://api.notion.com/v1/search" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "applicaion/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
query = ""
filter = @{
property = "object"
value = "database"
}
}) | Select-Object -ExpandProperty results | Where-Object object -EQ "database" | Select-Object id, @{n="title";e={ $_.title.plain_text }}
in my case it was
id | title |
---|---|
1d637d44-7d4b-80f8-b5a6-c9977db0f450 | movies |
note: technically it is in url, but without dashes
having database id in place we should be able to do some basic CRUD
create
Invoke-RestMethod -Method Post "https://api.notion.com/v1/pages" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
parent = @{ database_id = "1d637d44-7d4b-80f8-b5a6-c9977db0f450" }
properties = @{
Title = @{ title = @(@{ text = @{ content = "Inception" } }) }
Genres = @{ multi_select = @( @{ name = "Sci-Fi" }, @{ name = "Thriller" } ) }
Rating = @{ number = 8.8 }
Year = @{ number = 2010 }
Overview = @{ rich_text = @(@{ text = @{ content = "A thief who steals corporate secrets through dream-sharing tech is given the inverse task of planting an idea." } }) }
Poster = @{ files = @( @{ type = "external"; name = "poster.jpg"; external = @{ url = "https://image.tmdb.org/t/p/w500/xlaY2zyzMfkhk0HSC5VUwzoZPU1.jpg" } } ) }
}
})
Note: more details of how properties are composed can be found here
read
Invoke-RestMethod -Method Get "https://api.notion.com/v1/pages/1d637d44-7d4b-81dc-a4a3-f943559bd2d2" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" }
update
Invoke-RestMethod -Method Patch "https://api.notion.com/v1/pages/1d637d44-7d4b-81dc-a4a3-f943559bd2d2" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
parent = @{ database_id = "1d637d44-7d4b-80f8-b5a6-c9977db0f450" }
properties = @{
Title = @{ title = @(@{ text = @{ content = "Inception2" } }) }
# Genres = @{ multi_select = @( @{ name = "Sci-Fi" }, @{ name = "Thriller" } ) }
# Rating = @{ number = 8.8 }
Year = @{ number = 2020 }
# Overview = @{ rich_text = @(@{ text = @{ content = "A thief who steals corporate secrets through dream-sharing tech is given the inverse task of planting an idea." } }) }
# Poster = @{ files = @( @{ type = "external"; name = "poster.jpg"; external = @{ url = "https://image.tmdb.org/t/p/w500/74hLDKjD5aGYOotO6esUVaeISa2.jpg" } } ) }
}
})
Note: with Patch
you may update wanted fields
delete
Invoke-RestMethod -Method Patch "https://api.notion.com/v1/pages/1d637d44-7d4b-81dc-a4a3-f943559bd2d2" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{ archived = $true })
Now, when we have basic CRUD, lets fill the database with movies
seed
$movies = Get-Content movies.json | ConvertFrom-Json
foreach ($movie in $movies) {
Invoke-RestMethod -Method Post "https://api.notion.com/v1/pages" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
parent = @{ database_id = "1d637d44-7d4b-80f8-b5a6-c9977db0f450" }
properties = @{
Title = @{ title = @(@{ text = @{ content = $movie.title } }) }
Genres = @{ multi_select = $movie.genres | ForEach-Object { @{ name = $_ } } }
Rating = @{ number = $movie.rating }
Year = @{ number = $movie.year }
Overview = @{ rich_text = @(@{ text = @{ content = $movie.overview } }) }
Poster = @{ files = @( @{ type = "external"; name = "poster.jpg"; external = @{ url = $movie.poster } } ) }
}
})
}
and very last one
query
Invoke-RestMethod -Method Post "https://api.notion.com/v1/databases/1d637d44-7d4b-80f8-b5a6-c9977db0f450/query" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
filter = @{
and = @(
@{
property = "Genres"
multi_select = @{
contains = "Action"
}
}
@{
property = "Year"
number = @{
greater_than = 2020
}
}
)
}
}) | Select-Object -ExpandProperty results | Select-Object @{n='title';e={ $_.Properties.Title.title.plain_text }}, @{n='year';e={ $_.Properties.Year.number }}, @{n='genres';e={ $_.Properties.Genres.multi_select | Select-Object -ExpandProperty name }}
note: more on how to query fields can be found here
rich_text
note, rich text fields may contain basic markup
that's the reason why it is returned as array
once you have some markup it may look like:
[
{
"type": "text",
"text": {
"content": "The adventures of a group of explorers who make use of a newly "
}
},
{
"type": "text",
"text": {
"content": "discovered"
},
"annotations": {
"bold": true
}
},
{
"type": "text",
"text": {
"content": " wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage."
}
}
]
in example above, i have removed defaulted falsy properties, note how one bold word is extracted into dedicated array item.
while creating - same shape should be used.
children
each record in database is actual page, so it can contain any children
Invoke-RestMethod -Method Post "https://api.notion.com/v1/pages" -Headers @{ Authorization = "Bearer $($env:NOTION)"; "Notion-Version" = "2022-06-28" } -ContentType "application/json" -Body (ConvertTo-Json -Depth 100 -InputObject @{
parent = @{ database_id = "1d637d44-7d4b-80f8-b5a6-c9977db0f450" }
properties = @{
Title = @{ title = @(@{ text = @{ content = "Children2" } }) }
Genres = @{ multi_select = @( @{ name = "Sci-Fi" }, @{ name = "Thriller" } ) }
Rating = @{ number = 8.8 }
Year = @{ number = 2010 }
Overview = @{ rich_text = @(@{ text = @{ content = "A thief who steals corporate secrets through dream-sharing tech is given the inverse task of planting an idea." } }) }
Poster = @{ files = @( @{ type = "external"; name = "poster.jpg"; external = @{ url = "https://image.tmdb.org/t/p/w500/74hLDKjD5aGYOotO6esUVaeISa2.jpg" } } ) }
}
children = @(
@{
object = "block"
type = "paragraph"
paragraph = @{
rich_text = @(
@{
type = "text"
text = @{
content = "A thief who steals corporate secrets through dream-sharing tech is given the inverse task of planting an idea."
}
}
)
}
},
# header
@{
object = "block"
type = "heading_2"
heading_2 = @{
rich_text = @(
@{
type = "text"
text = @{
content = "Children"
}
}
)
}
}
# image
@{
object = "block"
type = "image"
image = @{
type = "external"
external = @{
url = "https://image.tmdb.org/t/p/w500/74hLDKjD5aGYOotO6esUVaeISa2.jpg"
}
}
}
)
})
note: technically this children is same as properties but can be anything