Contents
1. なぜ双方向同期が必要か ― メリットと全体像
| 項目 | 従来の課題 | 双方向同期で得られる効果 |
|---|---|---|
| 情報の一元化 | カレンダーは予定、Notion はタスクという別々の場所にデータが分散し、更新漏れが頻発 | 1つの操作だけで両方が自動的に最新状態になる |
| 手入力ミス防止 | イベント名や日時をコピー&ペーストすると文字化け・抜けが起きやすい | API 経由で正確なデータが転送されるためヒューマンエラーが激減 |
| リアルタイム通知 | Notion の変更は手動でカレンダーに反映しないとリマインドが届かない | 変更が即座に相手側へ反映され、Google カレンダーのプッシュ通知を活用できる |
| チーム共有コスト削減 | 各メンバーが別々に同じ情報を管理し、コンテキスト切り替えが必要 | 同一データソースで全員が閲覧・編集でき、コミュニケーションロスが減少 |
結論:Google カレンダーと Notion の双方向同期は「予定の可視化」と「タスク詳細管理」の両輪を同時に回すことができ、生産性向上に直結します。
2. 前提条件・準備(1 回だけ行えば OK)
2‑1. 必要なアカウントと権限
| サービス | 必要な権限 |
|---|---|
| Google アカウント | カレンダーの閲覧/編集権限(OAuth 2.0 のスコープ https://www.googleapis.com/auth/calendar) |
| Notion ワークスペース | Integration 作成権限、対象データベースへの Read と Update 権限 |
| Make または Zapier アカウント | 各サービスの API へ接続できるプラン(後述) |
2‑2. Notion API トークン取得手順
- Notion → 「設定 & メンバー」→「統合(Integrations)」
- New integration を作成し、名前とワークスペースを選択
- 権限は Read content と Update content のみをチェック → 「Submit」
- 表示された Internal Integration Token(
secret_…)をコピー
セキュリティ:トークンは必ず環境変数やシークレット管理ツールに保存し、コードベースにハードコーディングしないこと。
2‑3. Google カレンダー ID の取得
- Google カレンダーを開く → 左メニューの対象カレンダー右クリック → 「設定と共有」
- カレンダー ID(通常は
primaryまたはメールアドレス形式)をコピー
3. アーキテクチャ概要
|
1 2 3 4 |
[Google カレンダー] ←→ (Make / Zapier) ←→ [Notion データベース] ↑ ↓ API (OAuth2) API (Integration Token) |
- 双方向フロー:
- カレンダー → Notion(新規/更新イベント)
-
Notion → カレンダー(新規/更新ページ)
-
相互参照用 ID:Google の
eventIdと Notion のページ ID をそれぞれのレコードに保存し、重複作成を防止します。
4. 実装パターン比較 – Make(旧 Integromat) vs Zapier
4‑1. 料金・操作上限(2026 年時点)
| プラットフォーム | 無料プラン上限 | 有料プラン例 (月額) | 主な制約 |
|---|---|---|---|
| Make | 1,000 操作 / 月、15 分ポーリング間隔、100 MB データ転送 | Core $9 → 10,000 操作・10 分間隔 Pro $29 → 40,000 操作・5 分間隔 Team $99 → 150,000 操作・1 分間隔 |
シナリオは 1 つにつき最大 100 モジュール、操作数は「モジュール実行回数」 |
| Zapier | 100 タスク / 月、15 分ポーリング間隔 | Starter $19.99 → 3,000 タスク・5 分間隔 Professional $49.99 → 無制限タスク・2 分間隔 Team $299 → 50,000 タスク・1 分間隔 |
Zap は 1 つのフローにつき最大 20 ステップ、マルチステップは有料プランでのみ利用可 |
選択指針
- 少数イベント(月 200 件未満)→ Make の無料プランで十分。
- 高度な条件分岐・コードステップが必要 → Zapier Professional が便利。
4‑2. Google カレンダー API レートリミット(最新)
| 項目 | 上限 |
|---|---|
| 1日あたりのリクエスト数 | 1,000,000 リクエスト/プロジェクト |
| ユーザー単位の秒間クエリ | 10 クエリ/秒(バーストは最大 20) |
| デフォルトの配信遅延 | ポーリング型トリガーは最短 1 分(有料プラン) |
対策:Make/Zapier の「Throttle」や「Delay」モジュールで 200 ms〜500 ms 間隔を挟むと、429 エラーの回避に効果的です。
4‑3. Notion API レートリミット(最新)
| 項目 | 上限 |
|---|---|
| インテグレーション単位の秒間リクエスト | 3 リクエスト/秒、バースト最大 10 |
| データベースクエリ | 60 リクエスト/分 |
| 検索 API | 2,000 リクエスト/時 |
| 1日あたりの総リクエスト | 制限なし(ただし上記秒間・分間制限が実質的に抑制) |
対策:大量更新は「Batch Update」ではなく 1 件ずつ順次送信し、
sleep(200)ミリ秒程度のインターバルを設けると安定します。
5. Make を使った双方向同期シナリオ構築手順
ポイント:以下は「Core」プラン($9/月)前提です。無料プランでも動作しますが、ポーリング間隔は 15 分になる点だけ注意してください。
5‑1. シナリオ全体図
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[Google カレンダー: Watch Events] ──► Filter (新規/更新) ──► │ │ ▼ ▼ Create Notion Page Update Notion Page │ │ └─────► Add Text Property "Google Event ID" ◄───┘ [Notion: Watch Database Items] ──► Filter (新規/更新) ──► │ │ ▼ ▼ Create Google Event Update Google Event │ │ └─────► Add Description "Notion Page ID" ◄───┘ |
5‑2. 手順詳細
| ステップ | 操作内容 | 設定要点 |
|---|---|---|
| 1️⃣ シナリオ新規作成 | Make ダッシュボード → Create a new scenario | 名前は GC⇆Notion 双方向同期 推奨 |
| 2️⃣ Google カレンダーモジュール | Google Calendar – Watch Events を追加 | カレンダー ID = 取得した primary、間隔=5 分(Core) |
| 3️⃣ フィルタ―:新規 vs 更新 | 条件: event_id が空 → 新規、そうでなければ更新 |
初回実行は「Run once」で手動確認し、既存レコードに ID を付与 |
| 4️⃣ Notion – Create / Update Page | Notion – Create a Database Item または Update a Database Item | プロパティマッピング: ・ Title ← Event Summary ・ 開始日時 ← Start Time ・ 終了日時 ← End Time ・テキスト列 Google Event ID に {{event.id}} を保存 |
| 5️⃣ Notion → Google の逆ルート | 同様に Notion – Watch Database Items → フィルタ → Google Calendar – Create / Update Event | 作成時はイベント説明欄(description)に Notion Page ID を埋め込み、更新時はその ID で検索 |
| 6️⃣ データ変換 | Date formatter ユーティリティで YYYY-MM-DDTHH:mm:ssZ に統一 |
Google と Notion の ISO‑8601 が一致するようにタイムゾーンは必ず Asia/Tokyo 指定 |
| 7️⃣ レートリミット対策 | Throttle モジュールを 200 ms 間隔で追加 | 1 秒あたり最大 5 リクエストになるので、Notion の 3/sec 制限に余裕ができる |
| 8️⃣ テスト実行 & デバッグ | シナリオ左上の Run once → Google カレンダーにテストイベント作成 | Notion にページが生成されたら、逆方向も同様に検証 |
5‑3. コスト試算例(Core プラン)
| 想定件数 | 1 件あたりの操作数 | 月間総操作数 | 必要プラン |
|---|---|---|---|
| イベント 300 件/月 | 4 操作(Watch, Create, Update, Meta) | 1,200 | Core ($9) → 余裕あり |
| 大規模チーム 2,500 件 | 同上 | 10,000 | Pro ($29) が安全 |
6. Zapier を使った双方向同期構築手順
注意:Zapier の無料プランは「Google カレンダー → Notion」しかサポートしません。双方向にしたい場合は 2 本の Zap を作成します。
6‑1. Zap 構成概要
| Zap 名 | トリガー | アクション |
|---|---|---|
| Zap A: カレンダー → Notion | Google Calendar – New / Updated Event | Notion – Create / Update Database Item |
| Zap B: Notion → カレンダー | Notion – New / Updated Database Item | Google Calendar – Create / Update Detailed Event |
6‑2. 詳細設定
- Zap A(カレンダー→Notion)
- トリガーで「New Event」か「Updated Event」を選択。
- Filter ステップで
Google Event IDが Notion に存在しない場合のみ「Create」、存在すれば「Update」。 -
アクションの Notion – Create Database Item では、先ほど作成したデータベースに以下をマッピング:
・Title ← Summary
・開始日時 ← Start Time
・終了日時 ← End Time
・テキスト列 Google Event ID ←{{ID}} -
Zap B(Notion→カレンダー)
- トリガーは「New Database Item」または「Updated Database Item」。
- 同様に Filter で Notion の Google Event ID が空かどうかを判定。
-
アクションの Google Calendar – Create Detailed Event に以下を設定:
・Event Title ← Title
・Start / End ← Date properties(ISO‑8601)
・Description ←Notion Page ID: {{id}} -
ポーリング間隔調整
- Starter プラン以上で 5 分、Professional で 2 分、Team で 1 分。リアルタイム性が重要なら Professional 推奨。
6‑3. コスト試算例(Starter プラン)
| 月間タスク数 | 想定イベント件数 (双方向) | 必要タスク |
|---|---|---|
| 300 件 × 4 タスク/件 = 1,200 | Starter の 3,000 タスク上限で十分 | |
| 2,500 件 × 4 = 10,000 | Professional 無制限プランが必要 |
7. コードサンプル:API 直接呼び出しでカスタムロジック実装
7‑1. 共通前提 – 環境変数
|
1 2 3 4 5 |
# .env (例) NOTION_TOKEN=secret_XXXXXXXXXXXXXXXXXXXX NOTION_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx GOOGLE_CALENDAR_ID=primary |
Tip:Docker、Vercel、Cloud Functions などでデプロイする場合は必ずシークレット管理機能を利用してください。
7‑2. Node.js (Google → Notion)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// npm i @notionhq/client googleapis dotenv require('dotenv').config(); const { Client } = require('@notionhq/client'); const { google } = require('googleapis'); const notion = new Client({ auth: process.env.NOTION_TOKEN }); const calendar = google.calendar('v3'); // Google 認証はサービスアカウントか OAuth2.0 の取得済みトークンを使用 async function syncGoogleToNotion(event) { await notion.pages.create({ parent: { database_id: process.env.NOTION_DATABASE_ID }, properties: { Title: { title: [{ text: { content: event.summary } }], }, "開始日時": { date: { start: event.start.dateTime, time_zone: 'Asia/Tokyo' }, }, "終了日時": { date: { end: event.end?.dateTime, time_zone: 'Asia/Tokyo' }, }, "Google Event ID": { rich_text: [{ text: { content: event.id } }], }, }, }); } |
7‑3. Python (Notion → Google)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import os, requests, json from google.oauth2 import service_account from googleapiclient.discovery import build NOTION_TOKEN = os.getenv("NOTION_TOKEN") DB_ID = os.getenv("NOTION_DATABASE_ID") SCOPES = ["https://www.googleapis.com/auth/calendar"] SERVICE_ACCOUNT_FILE = "service-account.json" creds = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_FILE, scopes=SCOPES ) calendar_srv = build("calendar", "v3", credentials=creds) HEADERS = { "Authorization": f"Bearer {NOTION_TOKEN}", "Notion-Version": "2022-06-28", "Content-Type": "application/json", } def notion_page_to_event(page): props = page["properties"] title = props["Title"]["title"][0]["plain_text"] start = props["開始日時"]["date"]["start"] end = props["終了日時"]["date"].get("end", None) event_body = { "summary": title, "start": {"dateTime": start, "timeZone": "Asia/Tokyo"}, "end": {"dateTime": end or start, "timeZone": "Asia/Tokyo"}, "description": f"Notion Page ID: {page['id']}", } return event_body def sync_notion_to_google(page_id): # 取得 resp = requests.get( f"https://api.notion.com/v1/pages/{page_id}", headers=HEADERS ).json() event = notion_page_to_event(resp) # Google カレンダーへ作成(または更新) google_event_id = extract_google_id_from_notion(resp) # 任意実装 if google_event_id: calendar_srv.events().update( calendarId=os.getenv("GOOGLE_CALENDAR_ID"), eventId=google_event_id, body=event, ).execute() else: calendar_srv.events().insert( calendarId=os.getenv("GOOGLE_CALENDAR_ID"), body=event ).execute() |
レートリミット対策:上記サンプルは
time.sleep(0.2)(200 ms)をforループに入れるだけで、Notion の 3 req/sec 制限を回避できます。
8. 同期テスト・トラブルシューティング
8‑1. 推奨テストシナリオ
| テスト項目 | 手順 | 期待結果 |
|---|---|---|
| 基本新規作成 | Google カレンダーで「テスト会議」 (2026/06/10 14:00‑15:00) 作成 → 同期実行 | Notion に同名ページが生成、開始日時が日本時間に変換されている |
| タイムゾーン確認 | イベントを UTC (2026-07-01T02:00Z) で作成 → Notion 側は 2026-07-01T11:00+09:00 表示 |
時差が正しく反映される |
| 繰り返しイベント | 「毎週金曜 9:00‑10:00」設定 → 3 回分が個別ページとして生成 | 各回の開始日時が展開されたレコードになる |
| 双方向更新 | Notion のタイトルを手動で変更 → Google カレンダーのイベント名が即時に変わる | 双方向の「Update」ロジックが機能している |
| レートリミット耐性 | 100 件連続作成スクリプト実行 → エラーなしで完了 | Throttle/Delay が正しく働いている |
8‑2. よくあるエラーと対処
| エラーメッセージ | 原因例 | 解決策 |
|---|---|---|
| 401 Unauthorized (Google) | OAuth トークンの有効期限切れ | Google Cloud Console の「OAuth 同意画面」から再認可、またはサービスアカウントに切り替える |
| 429 Too Many Requests (Notion) | 1 秒間に 5 件以上リクエスト送信 | Throttle モジュールで最低 300 ms インターバルを設定、バッチ処理は分割 |
| Duplicate entry | 相互 ID が保存されていない状態で双方向作成が走る | 両側に必ず「Google Event ID」/「Notion Page ID」列を設け、フィルタで empty 判定を行う |
| タイムゾーンずれ | カレンダー側は UTC、Notion 側はローカル設定が未指定 | すべての日時フォーマットに time_zone: "Asia/Tokyo" を明示的に付与 |
9. セキュリティ・プライバシーのベストプラクティス
- 最小権限の原則
- Notion Integration は Read と Update のみ付与し、削除権限は付けない。
-
Google カレンダーは
https://www.googleapis.com/auth/calendar.events(イベント単体)に限定。 -
シークレット管理
-
GitHub/GitLab にトークンを書き込まない。CI/CD の Secret 機能、AWS Secrets Manager 等を使用。
-
アクセスログの監視
-
Make/Zapier の「Execution History」や Google Cloud Console の「Audit Logs」を週次で確認し、異常なリクエストが無いかチェック。
-
データ漏洩防止
- カレンダーは 非公開(共有設定は「自分のみ」)にし、外部リンクは生成しない。
-
Notion のページ ID は内部参照用なので、外部へ露出させない。
-
バックアップ戦略
- 定期的に Notion データベースを CSV エクスポート、Google カレンダーは iCal 形式でエクスポートし、万一のデータ破損に備える。
10. 発展的活用例
| シナリオ | 実装アイディア |
|---|---|
| プロジェクト全体スプリント管理 | Notion の「スプリント」データベースを基点に、開始・終了日を Google カレンダーのチームカレンダーへ自動投げる。変更は即時に Notion に反映し、ステータス更新だけで完結。 |
| コンテンツ制作パイプライン | 記事執筆 → 校正 → 公開 の各フェーズを Notion タスク化し、Google カレンダー上で「公開日」リマインドを自動設定。期限超過は Slack 通知へ連携可能。 |
| 社内会議室予約システム | 会議室ごとに Notion データベース(部屋名・キャパ)を作成、Google カレンダーで予約すると同時に Notion にブック情報が保存され、空き状況がリアルタイムで可視化。 |
| 学習ロードマップ | 大学の授業スケジュールは Google カレンダーから Notion の「科目」データベースへ自動インポート。課題期限は Notion → カレンダーに反映し、リマインドと進捗管理を一本化。 |
11. まとめと次のアクション
- 環境構築:Google と Notion の API トークン取得 → 環境変数に保存
- 自動化ツール選定:少量イベントなら Make 無料プラン、複雑ロジックや大量タスクは Zapier Professional を推奨
- シナリオ作成:この記事のフロー図を参考に 1 本の Make シナリオ(または 2 本の Zap)を構築
- テスト実行:基本・タイムゾーン・繰り返しイベントの 5 パターンを検証し、レートリミット対策が機能しているか確認
- 運用開始:定期的に実行履歴とログをレビューし、必要に応じて「Throttle」や「Delay」の間隔調整
最終的な効果:手入力の削減、情報散在の防止、リアルタイム通知という 3 本柱が揃い、個人でもチームでもタスク・スケジュール管理の生産性が大幅に向上します。
本稿は2026年4月時点の公式ドキュメント(Google Calendar API Quotas, Notion API Rate Limits, Make & Zapier 料金表)を基に作成しています。サービス仕様は予告なく変更される可能性があるため、導入前に最新版をご確認ください。