Skip to content

Instantly share code, notes, and snippets.

@BenjaminWolfe
Last active April 3, 2022 21:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BenjaminWolfe/0fa25dabe1dbedc32811a59196b5cdf1 to your computer and use it in GitHub Desktop.
Save BenjaminWolfe/0fa25dabe1dbedc32811a59196b5cdf1 to your computer and use it in GitHub Desktop.
Create Pages in Notion with R

To post a page to Notion.
Link: https://developers.notion.com/reference/post-page

The docs say it's a PUSH request. Here's the Shell example from the right side of the page. I've only edited the tab spacing to compress line widths.

curl 'https://api.notion.com/v1/pages' \
-H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
-H "Content-Type: application/json" \
-H "Notion-Version: 2022-02-22" \
--data '{
  "parent": { "database_id": "d9824bdc84454327be8b5b47500af6ce" },
  "icon": {
    "emoji": "🥬"
  },
  "cover": {
    "external": {
      "url": "https://upload.wikimedia.org/wikipedia/commons/6/62/Tuscankale.jpg"
    }
  },
  "properties": {
    "Name": {
      "title": [
        {
          "text": {
            "content": "Tuscan Kale"
          }
        }
      ]
    },
    "Description": {
      "rich_text": [
        {
          "text": {
            "content": "A dark green leafy vegetable"
          }
        }
      ]
    },
    "Food group": {
      "select": {
        "name": "Vegetable"
      }
    },
    "Price": { "number": 2.5 }
  },
  "children": [
    {
      "object": "block",
      "type": "heading_2",
      "heading_2": {
        "rich_text": [{ "type": "text", "text": { "content": "Lacinato kale" } }]
      }
    },
    {
      "object": "block",
      "type": "paragraph",
      "paragraph": {
        "rich_text": [
          {
            "type": "text",
            "text": {
              "content": "Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm.",
              "link": { "url": "https://en.wikipedia.org/wiki/Lacinato_kale" }
            }
          }
        ]
      }
    }
  ]
}'
# See `curl.md`. Here is the R equivalent.
library(httr)
library(jsonlite)
make_page <- function(url, headers, data) {
response <- POST(
url = url,
body = toJSON(data, auto_unbox = TRUE),
config = add_headers(.headers = headers)
)
content(response)
}
url <- "https://api.notion.com/v1/pages"
api_key <- "ADD API KEY HERE"
headers <- c(
"Authorization" = paste("Bearer", api_key),
"Content-Type" = "application/json",
"Notion-Version" = "2022-02-22"
)
data <- list(
parent = list(
database_id = "d9824bdc84454327be8b5b47500af6ce"
),
icon = list(
emoji = "🥬"
),
cover = list(
external = list(
url = "https://upload.wikimedia.org/wikipedia/commons/6/62/Tuscankale.jpg"
)
),
properties = list(
Name = list(
title = list(
list(
text = list(
content = "Tuscan Kale"
)
)
)
),
Description = list(
rich_text = list(
list(
text = list(
content = "A dark green leafy vegetable"
)
)
)
),
"Food group" = list(
select = list(
name = "Vegetable"
)
),
Price = list(
number = 2.5
)
),
children = list(
list(
object = "block",
type = "heading_2",
heading_2 = list(
rich_text = list(
list(
type = "text",
text = list(
content = "Lacinato kale"
)
)
)
)
),
list(
object = "block",
type = "paragraph",
paragraph = list(
rich_text = list(
list(
type = "text",
text = list(
content = paste(
"Lacinato kale is a variety of kale",
"with a long tradition in Italian cuisine,",
"especially that of Tuscany.",
"It is also known as Tuscan kale, Italian kale, dinosaur kale,",
"kale, flat back kale, palm tree kale, or black Tuscan palm."
),
link = list(
url = "https://en.wikipedia.org/wiki/Lacinato_kale"
)
)
)
)
)
)
)
)
make_page(url, headers, data)
# this script imported all 789 of my journal entries
# that I had previously exported from Daylio to CSV
# into Notion, using the Notion API.
# the project served as a great way to dive into
# using APIs with R, httr, and jsonlite.
library(here)
library(fs)
library(tidyverse)
library(lubridate)
library(jsonlite)
library(httr)
library(glue)
api_url <- "https://api.notion.com/v1"
api_key <- "ADD API KEY HERE"
database_id <- "ADD PARENT DATABASE ID HERE"
notion_version <- "2022-02-22"
headers <- c(
"Authorization" = paste("Bearer", api_key),
"Content-Type" = "application/json",
"Notion-Version" = notion_version
)
title_from_date <- function(x) {
# idea was to title entries with the date, including the weekday name
glue("{weekdays(x)} {month(x)}/{day(x)}/{year(x) %% 100}")
}
write_date <- function(date) {
# write dates to JSON in Notion's format with my time zone
# maybe this would work w/o being so explicit re: format_ISO8601()
# but I wanted to be sure
list(
date = list(
start = format_ISO8601(date),
time_zone = "America/Chicago"
)
)
}
write_select <- function(value) {
# a "select" field, as Notion takes them.
# used for my "mood"
list(
select = list(
name = value
)
)
}
write_tags <- function(tags) {
# a "multi-select" field, as Notion takes them.
# used for my "activities"
list(
multi_select = map(tags, ~list(name = .x))
)
}
write_title <- function(title) {
# a "title" field, as Notion takes them.
# used for my title, as created by title_from_date() above
list(
title = list(
list(
type = "text",
text = list(
content = title
)
)
)
)
}
write_content <- function(content) {
# a list of child blocks to populate a Notion page
# used for the content of my entries
map(
content,
~ list(
object = "block",
type = "paragraph",
paragraph = list(
rich_text = list(
list(
type = "text",
text = list(content = .x)
)
)
)
)
)
}
is_na <- function(x) {
# to avoid annoying warnings about is.na() on vectors.
# this function itself is not vectorized,
# and should only be used within a loop or mapping function.
if (length(x) > 1) return(FALSE)
return(is.na(x))
}
write_body <- function(date, mood, activities, title, entry) {
# put it all together to create the data to be sent, as a list.
# assemble properties: mandatory and optional
properties <- list(
title = write_title(title),
Entered = write_date(date)
)
if (!is.na(mood)) properties$Mood <- write_select(mood)
if (!is_na(activities)) properties$Activities <- write_tags(activities)
# assemble page: parent and properties
resp <- list(
parent = list(database_id = database_id),
properties = properties
)
# add content to page
if (!is_na(entry)) resp$children <- write_content(entry)
resp
}
send_entry <- function(body) {
# send the entry. return the Notion API response.
resp <- POST(
url = glue("{api_url}/pages"),
body = body,
config = add_headers(.headers = headers)
)
content(resp)
}
# read in my entries as exported from Daylio, process them, & export to Notion!
# save the API responses for reference and troubleshooting.
# activities are pipe-delimited.
# in entries, paragraphs are delimited by double spaces.
# every Notion response has an "object" key.
# in this case, object is either "page" or "error."
x <-
here() |>
dir_ls(type = "file", regexp = r"{daylio.*\.csv}") |>
read_csv() |>
arrange(full_date, time) |>
mutate(
date = ymd_hms(paste(full_date, time)),
activities = str_split(activities, r"{ \| }"),
title = title_from_date(date),
entry = str_split(note, " "),
data_list = pmap(list(date, mood, activities, title, entry), write_body),
body = map_chr(data_list, toJSON, auto_unbox = TRUE),
response = map(body, send_entry),
response_type = map_chr(response, ~.x$object)
) |>
select(-c(full_date, weekday, time, note, note_title))
saveRDS(x, file = here("responses.rds"))
save.image()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment