見出し画像

【GASで外部APIを叩く基本パターン】UrlFetchAppの使い方と認証の考え方

この記事で得られること

前回の記事ではGASでスプレッドシートの読み書き・カスタムメニュー・トリガーの基本を紹介しました。

今回は、GASの本当に強力な部分、外部APIとの連携を解説します。

UrlFetchAppを使えばスプレッドシートから外部サービスのデータを取得したり、他のサービスに情報を送信したりできます。
これができるとスプレッドシートが単なる表計算ソフトからデータ収集・連携ツールに化けます。

この記事では以下の内容をカバーします。

  • UrlFetchAppの基本的な使い方(GET / POST)

  • JSONレスポンスのパースとシートへの書き戻し

  • APIキーの安全な管理方法(PropertiesService)

  • OAuth認証の概念整理(何が難しいのか、をまず理解する)

実際に動くコードを使って説明するので手元で試しながら読んでもらえると一番理解しやすいです。



UrlFetchAppの基本

GETリクエスト

外部APIを叩く最もシンプルな形です。
ここでは無料で使える天気APIを例にします。

/**
 * Open-Meteo APIで東京の現在気温を取得する
 * (APIキー不要で試しやすい)
 */
function getWeather() {
  const url = 'https://api.open-meteo.com/v1/forecast'
    + '?latitude=35.6762&longitude=139.6503'
    + '&current_weather=true';

  const response = UrlFetchApp.fetch(url);
  const json = JSON.parse(response.getContentText());

  const weather = json.current_weather;
  Logger.log(`東京の気温: ${weather.temperature}°C`);
  Logger.log(`風速: ${weather.windspeed} km/h`);

  // スプレッドシートに書き込む
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  sheet.getRange('A1').setValue('取得日時');
  sheet.getRange('B1').setValue('気温(°C)');
  sheet.getRange('C1').setValue('風速(km/h)');
  sheet.getRange('A2').setValue(new Date());
  sheet.getRange('B2').setValue(weather.temperature);
  sheet.getRange('C2').setValue(weather.windspeed);
}

流れはシンプルで、UrlFetchApp.fetch(url)でHTTPリクエストを送りgetContentText()でレスポンスボディを文字列として取得、JSON.parse()でオブジェクトに変換するのが基本形です。

POSTリクエスト

データを送信する場合はPOSTを使います。
Slack Webhookへの通知を例にしてみましょう。

/**
 * Slack Incoming Webhookに通知を送る
 */
function sendSlackNotification(message) {
  const webhookUrl = PropertiesService.getScriptProperties().getProperty('SLACK_WEBHOOK_URL');

  const payload = {
    text: message
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  };

  const response = UrlFetchApp.fetch(webhookUrl, options);
  Logger.log('Slack送信結果: ' + response.getResponseCode());
}

optionsオブジェクトでmethod、contentType、payloadを指定します。
APIによってはheadersにAPI KeyやTokenを入れる場合もあります(後述)。


レスポンス処理の実践パターン

実際の開発では、APIレスポンスをそのままシートに書くケースは少なく、必要なフィールドだけ抽出して整形するのが普通です。

配列データをシートに一括書き込みするパターン

/**
 * APIで取得した配列データをスプレッドシートに整形して書き込む
 * (ダミーデータで構造を示す)
 */
function fetchAndWriteItems(items) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('データ') || ss.insertSheet('データ');

  // ヘッダー設定
  const headers = ['名前', '価格', 'カテゴリ', '取得日時'];
  sheet.getRange(1, 1, 1, headers.length).setValues([headers]);

  // データ整形:APIレスポンスから必要なフィールドだけ抽出
  const rows = items.map(item => [
    item.name || '不明',
    item.price ? Number(item.price) : 0,
    item.category || '未分類',
    new Date()
  ]);

  // 一括書き込み(前回の記事で触れた鉄則)
  if (rows.length > 0) {
    sheet.getRange(2, 1, rows.length, rows[0].length).setValues(rows);
  }

  Logger.log(`${rows.length}件のデータを書き込みました`);
}

ここで大事なのは防御的なデータ整形です。
APIレスポンスはドキュメントに書いてあるフィールドが実際にはnullだったり、そもそも存在しなかったりすることが日常的にあります。

item.name || '不明'`のようなフォールバック処理は常に入れておくべきです。


APIキーの安全な管理:PropertiesService

多くのAPIは認証にAPIキーを使います。
ここで絶対にやってはいけないのがコードにAPIキーをベタ書きすることです。

// ❌ これは絶対ダメ
const API_KEY = 'sk-abc123xxxxxxxxxxxxxxxxxx';

GASのスクリプトはスプレッドシートの共有設定に連動します。
シートを他の人と共有した瞬間APIキーが漏洩します。

PropertiesServiceを使う

GASにはPropertiesServiceというキー・バリュー型の保存領域があります。コードとは別の場所にAPIキーを保存できます。

/**
 * APIキーの初期設定(最初に1回だけ実行する)
 */
function setApiKeys() {
  const props = PropertiesService.getScriptProperties();
  props.setProperty('API_KEY', 'ここにAPIキーを貼る');
  // 設定後、この関数内のキー文字列は削除しておくこと
  Logger.log('APIキーを設定しました');
}

/**
 * APIキーの取得(実際の処理で使う)
 */
function callApiWithKey() {
  const apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');

  const url = `https://api.example.com/data?key=${apiKey}`;
  const response = UrlFetchApp.fetch(url);
  // ...
}

setApiKeys()を1回実行してキーを保存したら関数内のキー文字列はすぐ消してください。
実行ログにも残る可能性があるので設定後にログもクリアしておくのが理想です。

GUIから設定する方法

コードを経由せずに設定する方法もあります。

  1. スクリプトエディタの左メニュー「プロジェクトの設定」(歯車アイコン)

  2. 「スクリプト プロパティ」セクションで「スクリプト プロパティを追加」

  3. プロパティ名とAPIキーを入力して保存

こちらのほうが安全です。
キーがコードや実行ログに一切残りません。


認証の考え方:APIキーとOAuthの違い

外部APIを使うとき、認証方式は大きく分けて3つあります。

1. 認証なし

先ほどの天気API(Open-Meteo)のようにそもそも認証不要なAPI。
誰でもURLを叩けばデータが返ってきます。
無料の公開データ系に多いです。

2. APIキー認証

リクエストにAPIキーを含めて送る方式。
多くのAPIがこのパターンです。

// クエリパラメータで渡すパターン
const url = `https://api.example.com/data?apikey=${apiKey}`;

// ヘッダーで渡すパターン
const options = {
  headers: {
    'X-API-Key': apiKey
    // または 'Authorization': 'Bearer ' + apiKey
  }
};

どちらのパターンかはAPI仕様によります。
ヘッダーで渡すほうがURLにキーが露出しないためセキュリティ的にはベターですね。

3. OAuth 2.0

APIキーより複雑ですがより安全で柔軟な認証方式です。
大手サービスのAPI(Google、Twitter/X、eBay、Amazon等)はほぼこれです。

OAuth 2.0の基本的な流れは以下の通りです。

[あなたのアプリ]
    |
    | 1. Client ID + Client Secretを使ってトークンを要求
    v
[認証サーバー]
    |
    | 2. アクセストークンを発行
    v
[あなたのアプリ]
    |
    | 3. アクセストークンを使ってAPIを呼び出す
    v
[APIサーバー]
    |
    | 4. データを返す
    v
[あなたのアプリ]

APIキー認証との大きな違いはトークンに有効期限があることです。
期限が切れたら再取得が必要になります。

この「トークンの取得→キャッシュ→期限管理→再取得」のサイクルがOAuth実装で一番ハマりやすいポイントです。

OAuth 2.0のフロー種別

OAuthにはいくつかのフローがありますがGASで使うのは主に2つです。

Client Credentials Flow(アプリ単位の認証)

  • 用途:ユーザーの個人データにアクセスしない場合

  • 例:eBayの商品検索、公開データの取得

  • 特徴:実装がシンプル。GASとの相性が良い

Authorization Code Flow(ユーザー単位の認証)

  • 用途:特定ユーザーのデータにアクセスする場合

  • 例:Gmailの送受信、Googleドライブのファイル操作

  • 特徴:ユーザーの認可画面が必要。実装が複雑

APIが何のフローを求めているかはそのAPIのドキュメントに書いてあります。
「どのデータにアクセスしたいか」によってフローが決まるので、まず自分が何をしたいかを明確にしてからドキュメントを読むと迷いにくいですよ。


エラーハンドリングの基本

API連携ではネットワークエラー、認証エラー、レート制限など、失敗するケースが必ず発生します。
muteHttpExceptionsオプションとステータスコードの確認はセットで覚えてください。

/**
 * エラーハンドリング付きのAPI呼び出しテンプレート
 */
function fetchWithErrorHandling(url, options = {}) {
  // muteHttpExceptionsをtrueにすると、4xx/5xxでも例外を投げずにレスポンスを返す
  options.muteHttpExceptions = true;

  try {
    const response = UrlFetchApp.fetch(url, options);
    const statusCode = response.getResponseCode();
    const body = response.getContentText();

    switch (true) {
      case (statusCode === 200):
        return JSON.parse(body);

      case (statusCode === 401):
        throw new Error('認証エラー: APIキーまたはトークンを確認してください');

      case (statusCode === 403):
        throw new Error('権限エラー: このリソースへのアクセス権がありません');

      case (statusCode === 429):
        throw new Error('レート制限: 時間を空けて再実行してください');

      case (statusCode >= 500):
        throw new Error(`サーバーエラー (${statusCode}): API側の問題です。時間を空けて再実行してください`);

      default:
        throw new Error(`予期しないエラー (${statusCode}): ${body}`);
    }
  } catch (e) {
    // ネットワークエラー等、fetch自体が失敗した場合
    Logger.log('API呼び出し失敗: ' + e.message);
    throw e;
  }
}

muteHttpExceptions: trueを設定しないと4xxや5xxのレスポンスが来た時点で例外が投げられレスポンスボディ(エラーの詳細情報)を確認できません。
API連携では常にtrueにしておきステータスコードで自分で分岐するのがセオリーです。


実践例:為替レートを定期取得してシートに記録する

ここまでの内容を組み合わせた実践例です。
無料の為替APIから日次でレートを取得しスプレッドシートに蓄積していきます。

/**
 * USD/JPYの為替レートを取得して記録する
 */
function recordExchangeRate() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('為替ログ') || ss.insertSheet('為替ログ');

  // ヘッダーがなければ設定
  if (sheet.getRange('A1').getValue() === '') {
    sheet.getRange('A1:C1').setValues([['日時', 'USD/JPY', '取得元']]);
  }

  // Open Exchange Rates API(フリープラン)
  const apiKey = PropertiesService.getScriptProperties().getProperty('EXCHANGE_API_KEY');
  const url = `https://openexchangerates.org/api/latest.json?app_id=${apiKey}&symbols=JPY`;

  try {
    const data = fetchWithErrorHandling(url);
    const rate = data.rates.JPY;

    // 最終行の次に追記
    const nextRow = sheet.getLastRow() + 1;
    sheet.getRange(nextRow, 1, 1, 3).setValues([
      [new Date(), rate, 'Open Exchange Rates']
    ]);

    Logger.log(`USD/JPY: ${rate} を記録しました`);
  } catch (e) {
    Logger.log('為替レート取得に失敗: ' + e.message);
    // 失敗しても既存データは壊さない
  }
}

この関数を前回紹介したトリガーで毎日実行すれば為替レートの推移が自動でスプレッドシートに蓄積されていきます。


まとめ

この記事で紹介した内容を整理するとGASでAPI連携する際の基本は以下の通りです。

  • UrlFetchApp.fetch()でHTTPリクエストを送る(GET/POST)

  • レスポンスは**JSON.parse()**でオブジェクトに変換し、必要なフィールドだけ抽出する

  • APIキーは**PropertiesService**で管理する(コードにベタ書きは厳禁)

  • **muteHttpExceptions: true**を設定して、ステータスコードで自分でエラーハンドリングする

  • OAuth 2.0は「トークンの取得→キャッシュ→期限管理→再取得」のサイクルが肝

ここまでの基礎があれば、あとはAPIのドキュメントを読んで対象サービスに合わせるだけです。

ただし実際にOAuth 2.0を実装するとドキュメントに書いていないハマりポイントが出てきます。
スコープ指定の罠、トークンキャッシュの戦略、レート制限への対策などなど。

こうした実践的な部分は公式ドキュメントだけではなかなか解決できません。

次の記事ではeBay Browse APIを題材にOAuth 2.0認証の実装からレート制限対策まで、実際のハマりどころをコード付きで詳しく解説しています。


いいなと思ったら応援しよう!