8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

こんにちは。3年目のアドベントカレンダー参戦になります。株式会社ピー・アール・オーです。
これまでに引き続き、今年も各自が興味のある分野について各々に書く方針で進めさせていただきます。
これまでと同様に、とりとめのない内容になるとは思いますが、ご査収いただければ幸いです。

Notion APIが使えるようになりました(半年前に)

昨年のアドベントカレンダーでNotionに関する記事をいくつか挙げたんですが、その際にはまだ公式のAPIがなくて、Notionに可能性を感じながらも利用にあたってはやや遠回りをせざるを得ないのが残念に感じていました。
ですが、2021/5/11にかねてからその開発が噂されていたAPIがPublic Betaとして公開されました。日本語の情報としてもこのころにAPI試してみた系の記事が多く登場していましたので、Notion使いの方たちはきっと目にされていたことと思います。
本記事執筆時点でもまだPublic Betaのままのようですが、後述するようにNotion APIは日々機能追加が行われており、公開当初に書かれた内容とは状況が異なっているものや新たに追加された機能が散見されましたので、あらためて現時点のAPIの状況について私の視点から試してまとめてみたというのが本記事の趣旨となります。

はじめてみる

integrationの作成

利用開始の手順については公開当初と大きくは変わっていないようです。まずはNotionにログインした状態で、https://www.notion.so/my-integrationsにアクセスします。
image.png
New Integrationを選択して情報を入力します。
image.png
IntegrationはPublicを選ぶとOAuth2.0ベースのアクセストークンを利用したアクセスとなるようです。
今回はお試しなのでInternalで。
image.png

submitするとSecretsが表示されます。これは後程APIを叩く際に必要なのでコピっておきましょう。
image.png

Notionアプリ側の設定

Integrationが追加されると、NotionのSetting & Members > Integrationsにも作成したものが表示されるようになります。
image.png
あとは、実際にAPIを利用したいPageでShare⇒Inviteを選び、作成したIntegrationを選択すれば準備完了です。
image.png
Inviteした結果。他のユーザーに対してShareするのと同じような感じですね。
image.png

APIを叩いてみる

page

ではさっそくCurlで叩いてみましょう。叩くにあたって必要な情報はBarer TokenとPageIDになります。
Barer TokenはIntegration設定時に表示されたSecretsになります。PageIDはページの「share」から表示されるウインドウで、Copy linkすることで得ることができます。
image.png
実際に得られるURLは↓のような感じですが、PageIDは末端のハッシュ値が相当するようです。

ちなみに下のハッシュ値は改ざんしています。本記事内の他のハッシュ値も値変えていますので実際の値と異なります。

image.png
Getting Startedのページを取得してみます。

curl 'https://api.notion.com/v1/blocks/{PageID}/children?page_size=100' \
  -H 'Authorization: Bearer '"{Secret}"'' \
  -H "Notion-Version: 2021-08-16"

Notion-VersionというのはAPIのリリース日で管理されているようですね。最新はここで確認できるようです。

https://developers.notion.com/reference/versioning

取得結果は以下のようになります。

{
	"object": "list",
	"results": [
		{
			"object": "block",
			"id": "96a12436-676f-4ec0-a3ba-8de4g4hbdb4a",
			"created_time": "2020-10-17T05:36:00.000Z",
			"last_edited_time": "2020-10-17T05:36:00.000Z",
			"has_children": false,
			"archived": false,
			"type": "paragraph",
			"paragraph": {
				"text": [
					{
						"type": "text",
						"text": {
							"content": "👋 Welcome to Notion!",
							"link": null
						},
						"annotations": {
							"bold": false,
							"italic": false,
							"strikethrough": false,
							"underline": false,
							"code": false,
							"color": "default"
						},
						"plain_text": "👋 Welcome to Notion!",
						"href": null
					}
				]
			}
		},
		{
			"object": "block",
			"id": "8ef56ed9-1ad0-424d-a887-b3674cb6a506",
			"created_time": "2020-10-17T05:36:00.000Z",
			"last_edited_time": "2020-10-17T05:36:00.000Z",
			"has_children": false,
			"archived": false,
			"type": "paragraph",
			"paragraph": {
				"text": []
			}
		},
        (以下省略)

blockってのはNotionのページ内における行を表しているようです。上のJSONに対応するのはこの部分ですね。
image.png

画像やCodeブロックなどはどうでしょうか?いくつかのAPI試してみた系の記事では取得ができないという話だったので気になっていましたが・・・
image.png
最新のAPIではどちらも問題なく取得できるようになったようです。

database

さて、普通のPageについてはblocksで返されるJSONは大変納得がいくものでしたが、notionの売りであるDatabaseに対して実施するとどうなるのでしょう?
Notionで管理しているDatabaseとして、私のお弁当レシピリストがありました。
極めてプライベートな情報のため気恥ずかしいですが、ためしにそれを対象にしてみましょう。
image.png
上記Pageに対してまずはblockを取得してみます。

{
	"object": "list",
	"results": [
		{
			"object": "block",
			"id": "a98e0e05-d915-406a-a987-bdf775a8be24",
			"created_time": "2021-10-22T09:58:00.000Z",
			"last_edited_time": "2021-11-25T03:39:00.000Z",
			"has_children": false,
			"archived": false,
			"type": "child_database",
			"child_database": {
				"title": ""
			}
		},
		{
			"object": "block",
			"id": "bb126f57-3078-4d80-ac10-b9edd0bb6134",
			"created_time": "2021-10-22T10:00:00.000Z",
			"last_edited_time": "2021-10-22T10:00:00.000Z",
			"has_children": false,
			"archived": false,
			"type": "paragraph",
			"paragraph": {
				"text": []
			}
		}
	],
	"next_cursor": null,
	"has_more": false
}

まあ案の定ですが、DatabaseはPage内コンテンツ(block)とは管理が別なんですね。
しかもdatabaseを配置しているPage内情報からはそのDatabaseのIDが引けないようです。仕方ないのでPageと同様にDatabaseからCopy libkでIDを取得します。
image.png

GETメソッドのdatabasesを叩いてみます。

curl 'https://api.notion.com/v1/databases/{databaseID}' \
  -H 'Authorization: Bearer '"{secret}"'' \
  -H "Notion-Version: 2021-08-16"

あれ?意外に簡素な内容しか取れないですね・・・

{
    "object": "database",
    "id": "a98e3e05-d913-305a-a951-bdf867a8fg35",
    "cover": null,
    "icon": null,
    "created_time": "2021-10-22T09:58:00.000Z",
    "last_edited_time": "2021-11-25T03:39:00.000Z",
    "title": [],
    "properties": {
        "Date": {
            "id": "sN_~",
            "name": "Date",
            "type": "date",
            "date": {}
        },
        "Name": {
            "id": "title",
            "name": "Name",
            "type": "title",
            "title": {}
        }
    },
    "parent": {
        "type": "page_id",
        "page_id": "cbb47508-34da-48ca-a0d2-fd496013109a"
    },
    "url": "https://www.notion.so/a99e5e05d915406ab875bdf775a8be24"
}

どうやらdatabaseの中身を参照したいときには単なるGETではだめで、POSTメソッドであるqueryで検索条件を渡さないとダメみたいですね。

https://developers.notion.com/reference/post-database-query

と、いうわけで改めて・・・

curl -X POST 'https://api.notion.com/v1/databases/{databaseID}/query' \
  -H 'Authorization: Bearer '"{secret}"'' \
  -H 'Notion-Version: 2021-08-16' \
  -H "Content-Type: application/json" \
	--data '{
    "filter": {
        "or": [
            {
                "property": "Date",
                "date": {
                    "after": "2021-11-01"
                }
            }
        ]
    },
    "sorts": [
        {
            "property": "Date",
            "direction": "ascending"
        }
    ]
}'

おお、取得できました!

{
    "object": "list",
    "results": [
        {
            "object": "page",
            "id": "666f2743-08a5-4e03-7966-145b6cbcd0c1",
            "created_time": "2021-11-05T07:30:00.000Z",
            "last_edited_time": "2021-11-05T23:58:00.000Z",
            "cover": null,
            "icon": null,
            "parent": {
                "type": "database_id",
                "database_id": "a98e1e05-d914-405a-a951-bdf886a8fg25"
            },
            "archived": false,
            "properties": {
                "Date": {
                    "id": "sN_~",
                    "type": "date",
                    "date": {
                        "start": "2021-11-08",
                        "end": null
                    }
                },
                "Name": {
                    "id": "title",
                    "type": "title",
                    "title": [
                        {
                            "type": "text",
                            "text": {
                                "content": "紅しょうがと大葉の豚こま天ぷらと卵の天ぷら",
                                "link": null
                            },
                            "annotations": {
                                "bold": false,
                                "italic": false,
                                "strikethrough": false,
                                "underline": false,
                                "code": false,
                                "color": "default"
                            },
                            "plain_text": "紅しょうがと大葉の豚こま天ぷらと卵の天ぷら",
                            "href": null
                        }
                    ]
                }
            },
            "url": "https://www.notion.so/666f274408a74e058293234b6aabf9c1"
        },
        {
            "object": "page",
            "id": "2db12d01-447d-47fd-83bc-23793bc4ad90",
            "created_time": "2021-11-05T22:34:00.000Z",
            "last_edited_time": "2021-11-09T00:50:00.000Z",
            "cover": null,
            "icon": null,
            "parent": {
                "type": "database_id",
                "database_id": "a98e1e05-d914-405a-a951-bdf886a8fg25"
            },
            "archived": false,
            "properties": {
                "Date": {
                    "id": "sN_~",
                    "type": "date",
                    "date": {
                        "start": "2021-11-09",
                        "end": null
                    }
                },
                "Name": {
                    "id": "title",
                    "type": "title",
                    "title": [
                        {
                            "type": "text",
                            "text": {
                                "content": "昆布だし塩サバ",
                                "link": null
                            },
                            "annotations": {
                                "bold": false,
                                "italic": false,
                                "strikethrough": false,
                                "underline": false,
                                "code": false,
                                "color": "default"
                            },
                            "plain_text": "昆布だし塩サバ",
                            "href": null
                        }
                    ]
                }
            },
            "url": "https://www.notion.so/2db13d01556556fe82bc14243bc3ad90"
        },
        (省略)

ちなみに、dateはISO8601書式が使えるようです。したがって日本時間で検索したいときには、

curl -X POST 'https://api.notion.com/v1/databases/a98e0e05d915406aa951bdf775a8be24/query' \
  -H 'Authorization: Bearer '"secret_4P4vCb0DLbqXbrbQWDhH3RC14fuR5xXP2014vNssfsK"'' \
  -H 'Notion-Version: 2021-08-16' \
  -H "Content-Type: application/json" \
	--data '{
    "filter": {
        "or": [
            {
                "property": "Date",
                "date": {
                    "after": "2021-11-01T00:00:00+09:00"
                }
            }
        ]
    },
    "sorts": [
        {
            "property": "Date",
            "direction": "ascending"
        }
    ]
}'

こんな感じの指定ができるってわけですね。この機能はつい最近(先月?)追加されたようで、Notion APIは今ももりもり開発されているようです。

計算式を含むDatabase

もう少し複雑なdatabaseに対してやってみます。
notionは簡単な数式を設定できるのですが、数式を設定したもう少し複雑なパターンはどうでしょうか?
↓こんなDatabseを対象にしてみます。
image.png

実行結果

    "object": "list",
    "results": [
        {
            "object": "page",
            "id": "d2e6af07-df0e-43e3-a1f6-320542f1d321",
            "created_time": "2021-11-25T10:07:00.000Z",
            "last_edited_time": "2021-11-25T10:07:00.000Z",
            "cover": null,
            "icon": {
                "type": "emoji",
                "emoji": "📃"
            },
            "parent": {
                "type": "database_id",
                "database_id": "9e0e27f8-e435-4c32-76e5-c75173b0ec08"
            },
            "archived": false,
            "properties": {
                "Type": {
                    "id": "R%3DsB",
                    "type": "multi_select",
                    "multi_select": [
                        {
                            "id": "4c816f99-0e87-508a-8cb1-89d28577c8fa",
                            "name": "Task",
                            "color": "green"
                        }
                    ]
                },
                "Related to サブタスク (Column)": {
                    "id": "UYws",
                    "type": "relation",
                    "relation": [
                        {
                            "id": "f126c964-de6e-48cc-b972-c16b569a3b31"
                        },
                        {
                            "id": "e756b1b9-96ef-45e8-a765-2698b0a08466"
                        },
                        {
                            "id": "fa54136a-fa45-3189-beb1-53d51c84bd45"
                        },
                        {
                            "id": "5ec97606-954f-3654-8850-1ceebb2c688e"
                        }
                    ]
                },
                "進捗バー": {
                    "id": "buAP",
                    "type": "formula",
                    "formula": {
                        "type": "string",
                        "string": "▓▓▓▓▓▓▓▓▓▓ 100%"
                    }
                },
                "Assign": {
                    "id": "e_VQ",
                    "type": "people",
                    "people": [
                        {
                            "object": "user",
                            "id": "7a87b8fc-e99f-4c19-9cb3-26f6c61109b",
                            "name": "matuzaki",
                            "avatar_url": "https://lh3.googleusercontent.com/-7W9F5KJAcnI/AAAAAAAAAAI/AAAAAAAAAAA/AMZvvbmSP7V82UpKTlMeDQ2IhEHx5KOe0Q/photo.jpg",
                            "type": "person",
                            "person": {
                                "email": "xxxxxxxxxx"
                            }
                        }
                    ]
                },
                "各タスク状況": {
                    "id": "lwBb",
                    "type": "rollup",
                    "rollup": {
                        "type": "number",
                        "number": 1,
                        "function": "percent_checked"
                    }
                },
                "Date": {
                    "id": "zANe",
                    "type": "date",
                    "date": {
                        "start": "2021-11-05",
                        "end": "2021-11-09"
                    }
                },
                "Status": {
                    "id": "zHyE",
                    "type": "select",
                    "select": {
                        "id": "0ee89e54-1ae4-3987-ae3b-a975073d4439",
                        "name": "Pending",
                        "color": "blue"
                    }
                },
                "Name": {
                    "id": "title",
                    "type": "title",
                    "title": [
                        {
                            "type": "text",
                            "text": {
                                "content": "設計書フォーマット整理",
                                "link": null
                            },
                            "annotations": {
                                "bold": false,
                                "italic": false,
                                "strikethrough": false,
                                "underline": false,
                                "code": false,
                                "color": "default"
                            },
                            "plain_text": "設計書フォーマット整理",
                            "href": null
                        }
                    ]
                }
            },
            "url": "https://www.notion.so/d2e6af07df0e31c3a1e7320542f1d253"
        },
        (省略)

プロパティで、数式にしている個所は演算結果のみ取得できるようですね。
サンプルで進捗バーはformulaで定義しているんですが、結果のバーのみが取得できました。
その他はおおむね情報としては取得できてそうな感じです。
image.png

利用できるAPI一覧

notion公式に上がってるAPIは執筆時点で以下となります。
まだ必要最低限といった感じですが、一通りのCRUD操作的なものは出来そうです。
ちなみに、Retrieve~ってのはGETなので取得メソッドですね。

Category API Method
Database object Query a database POST
Create a database POST
Update database PATCH
Retreive a database GET
List database(deprecated) GET
Page object Retrieve a page GET
Create a page POST
Update page PATCH
Archive a Page(delated)
Retrieve a page property item GET
Block object Retrieve a block GET
Update a block PATCH
Retrieve block children GET
Append block children PATCH
Delete a block DELETE
User object Retrieve a user GET
List all users GET
Retrieve your token's bot user GET
Search Search

所感

notion API、まだまだ発展途上ながら、APIが出た当初に比べてかなり拡充されきていると言えそうです。
まだまだ開発も続いているようなので、まだ機能がもの足りないというようなときはchange logをみてみるといいかもしれません。
1week程度の周期で結構新機能がリリースされてたりします。

本記事が皆様のnotion活用に寄与できれば幸いです。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?