Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第8章 エラーハンドリングと運用のベストプラクティス

Notion API を安定して運用するためのエラーハンドリング手法、レートリミット対策(指数バックオフ)、およびセキュリティ面での注意点を解説します。

主要な HTTP ステータスコードとエラー種別

Notion API で問題が発生した場合、標準的な HTTP ステータスコードとともに、JSON 形式のエラー詳細が返されます。

HTTP コードNotion エラーコード主な原因と対策
400validation_errorリクエストの JSON 形式やプロパティの型が不正。型定義を見直す。
401unauthorizedトークンが無効または指定されていない。.env の値を確認する。
404object_not_found指定した ID のページ・DB が存在しない、またはインテグレーションが未接続。
429rate_limitedリクエスト頻度が高すぎる。少し待機してから再試行する。
500 / 502internal_server_errorNotion 側のサーバー障害。時間をおいて再試行する。

SDK によるエラーハンドリングの実装

notion-client を使用する場合、API の失敗は APIResponseError 例外として発生します。 例外をキャッチすることで、プログラムが異常終了するのを防ぎ、適切な対処を行えます。

import os
from dotenv import load_dotenv
from notion_client import Client
from notion_client.errors import APIResponseError

load_dotenv()
notion = Client(auth=os.getenv("NOTION_TOKEN"))

try:
    response = notion.pages.retrieve(page_id="invalid_page_id")
except APIResponseError as error:
    print(f"Notion API エラー検知!")
    print(f"ステータスコード: {error.status}")
    print(f"エラーコード: {error.code}")
    print(f"メッセージ: {error.message}")
except Exception as e:
    print(f"予期しないエラー: {e}")

レートリミット(429)と指数バックオフ

Notion API の制限目安は「1 秒あたり平均 3 リクエスト」です。 大量のデータを一度に更新・取得しようとすると、429 Rate Limited エラーが発生することがあります。

429 エラーが返された場合は、レスポンスヘッダーの Retry-After(秒数)を参照して待機するか、徐々に待機時間を延ばしながら再試行する 指数バックオフ(Exponential Backoff) を実装します。

import time
import requests

def request_with_retry(url, headers, max_retries=5):
    delay = 1.0  # 初期待機時間(秒)
    
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        
        # 成功した場合はレスポンスを返す
        if response.status_code == 200:
            return response.json()
            
        # レートリミットの場合は待機して再試行
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", delay))
            print(f"レートリミット発生。{retry_after} 秒待機します(試行 {attempt + 1}/{max_retries})")
            time.sleep(retry_after)
            delay *= 2  # 待機時間を倍増
            continue
            
        # その他のエラーは中断
        response.raise_for_status()
        
    raise RuntimeError("最大再試行回数を超過しました。")

運用のセキュリティ・ベストプラクティス

  1. シークレットキーの分離: トークンはソースコードに直接書かず、環境変数やシークレット管理ツールで保持します。
  2. 接続権限の最小化: インテグレーションには必要なページやデータベースのみを接続し、ワークスペース全体への無制限なアクセスを避けます。
  3. ログ出力時のマスキング: トークンや個人情報(メールアドレス等)がログファイルに出力されないよう配慮します。
  4. 定期的な棚卸し: 使わなくなったインテグレーションや接続権限は、開発者ポータルおよびページの設定から削除します。