Google Search Console API を GAS で叩ける“エンドポイント”を作る方法-2025/12実際の作業環境で遭遇した罠まとめ&コード

この記事の内容について、業務や開発でお困りの場合は個別に対応できます。

Google Search Console(GSC)のクエリデータを、GAS(Google Apps Script)から直接取得できれば、ChatGPT のカスタム指示や WordPress の自動分析にも活用できます。しかし、実際にやってみると「高度なサービスに Search Console が出ない」「OAuth2 ライブラリが見つからない」「プロジェクト ID と番号を間違える」といった、初心者が確実に詰まる罠が連続します。

すみませんが、今回結構上級モードです・・・

この記事では、実際に私が遭遇したエラーや詰まりポイントをすべて踏まえつつ、最短で Search Console API を叩ける GAS エンドポイントを作る方法をまとめます。

結論:Search Console API は“高度なサービスに存在しない”。OAuth2 + UrlFetch で叩くのが正攻法

BigQuery や Drive API などと違い、GAS の「高度なサービス」には Search Console API が出てきません。
そのため、GSC を GAS から扱う場合は、以下の構造が必須です。

  • Google Cloud Platform(GCP)で Search Console API を有効化
  • OAuth2 クライアントを作成
  • GAS に OAuth2 ライブラリを追加
  • UrlFetchApp で Search Console API を叩く

ここを理解しておくと、無駄にサービスを探し回る時間がゼロになります。

↑有効化したのに

↑サービスにはでないです!!!😿

↑GASとGCPとの接続とかもした。ちなみに番号であってIDじゃないので注意!
■ 手順1:Google Cloud Platform で Search Console API を有効化する

まず https://console.cloud.google.com/ にアクセスし、利用したい GCP プロジェクトで Search Console API を有効化します。

  • 「API とサービス」→「ライブラリ」
  • “Google Search Console API” を検索して有効化

(画像:有効化済み API の一覧に Search Console が表示されている画面)


■ 手順2:GAS 側のプロジェクト設定を“正しい GCP プロジェクト番号”に切り替える

ここが今回最大のつまずきポイントでした。

  • Apps Script の「設定」→「Google Cloud Platform プロジェクト」
  • 「変更」をクリック
  • GCP プロジェクト番号 を入力

この“番号”と“ID”の違いに気づかないと、OAuth や API が正しく連動しません。

ここほんとに注意!手順1をかならずやること!!!


■ 手順3:OAuth2 ライブラリを GAS に追加する(正しい Script ID を使う)

GSC API は OAuth 認証が必須です。
そのため、GAS へ OAuth2 ライブラリを追加します。

正しいスクリプト ID:

1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

(画像:ライブラリ追加画面で ID を入れるも検索できず困っているところ)

この ID を入れると「OAuth2」というライブラリが出てきます。

■ 手順4:GCP で OAuth クライアントを作成(リダイレクト URI が重要)

GCP の「APIとサービス → 認証情報 → 認証情報を作成 → OAuth クライアント ID」からクライアントを作成し、リダイレクト URI を登録します。

登録する URI は以下:

https://script.google.com/macros/d/<スクリプトID>/usercallback

これを忘れると認証画面で必ずエラーになります。

ここらへんネットにいろいろ情報あるので調べながらやるといいと思います!

■ 手順5:GAS に貼るコード(そのまま動くテンプレート)

以下のコードを GAS に貼り付けるだけで、認証~GSC API の取得~エンドポイント化まで完結します。

// ==== 設定 ==== 
const CLIENT_ID     = 'あなたのクライアントID';
const CLIENT_SECRET = 'あなたのクライアントシークレット';
const SITE_URL      = 'あなたのサイトURL';

// ==== OAuth2 サービス ==== 
function getService() {
  return OAuth2.createService('gsc')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://oauth2.googleapis.com/token')
    .setClientId(CLIENT_ID)
    .setClientSecret(CLIENT_SECRET)
    .setCallbackFunction('authCallback')
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope('https://www.googleapis.com/auth/webmasters.readonly')
    .setParam('access_type', 'offline')
    .setParam('prompt', 'consent');
}

function authCallback(request) {
  const service = getService();
  const authorized = service.handleCallback(request);
  return HtmlService.createHtmlOutput(
    authorized ? 'Authorization successful! You can close this tab.'
               : 'Authorization denied.'
  );
}

// ==== GSC データ取得テスト ====
function testGSC() {
  const service = getService();
  if (!service.hasAccess()) {
    Logger.log('認証URL: ' + service.getAuthorizationUrl());
    return;
  }

  const endpoint =
    'https://searchconsole.googleapis.com/webmasters/v3/sites/' +
    encodeURIComponent(SITE_URL) +
    '/searchAnalytics/query';

  const body = {
    startDate: '2025-12-01',
    endDate:   '2025-12-11',
    dimensions: ['query'],
    rowLimit: 10
  };

  const res = UrlFetchApp.fetch(endpoint, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(body),
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken()
    }
  });

  Logger.log(res.getContentText());
}

// ==== 公開エンドポイント ====
function doGet(e) {
  const service = getService();
  if (!service.hasAccess()) {
    return HtmlService.createHtmlOutput(
      '<a href="' + service.getAuthorizationUrl() + '">Google Search Console へのアクセスを許可</a>'
    );
  }

  const today = new Date();
  const endDate = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy-MM-dd');
  const start = new Date(today);
  start.setDate(start.getDate() - 7);
  const startDate = Utilities.formatDate(start, 'Asia/Tokyo', 'yyyy-MM-dd');

  const endpoint =
    'https://searchconsole.googleapis.com/webmasters/v3/sites/' +
    encodeURIComponent(SITE_URL) +
    '/searchAnalytics/query';

  const body = {
    startDate,
    endDate,
    dimensions: ['query'],
    rowLimit: 50
  };

  const res = UrlFetchApp.fetch(endpoint, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(body),
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken()
    }
  });

  return ContentService
    .createTextOutput(res.getContentText())
    .setMimeType(ContentService.MimeType.JSON);
}

■ 手順6:Webアプリとして公開 → 完成!

右上「デプロイ」→「新しいデプロイ」
種類:ウェブアプリ

  • 実行者:自分
  • アクセス権:全員(匿名も可)

■ 最終的に何ができるようになるか?

ChatGPT カスタム指示から叩いて“リアルタイムの GSC クエリ”を渡せる!!

もちろんコードは適宜いじってくださいね

―――

■追記:関連エラー

■ 1. redirect_uri_mismatch

● 発生するケース

  • OAuth クライアントに登録した リダイレクトURI が GAS 側と一致していない
  • usercallback パスが抜けている
  • スクリプトIDを間違えている(プロジェクト番号と混同)

● 正しい URI の例

https://script.google.com/macros/d/<スクリプトID>/usercallback

● 原因

  • 「プロジェクト番号」を「スクリプトID」と勘違い
  • GAS のデプロイを作り直したあと、古い URI を登録し続けている
  • Google Cloud 側の OAuth 設定を更新し忘れ

● 対処

  • GCP に登録している URI と GAS のスクリプトID を必ず一致させる
  • デプロイをし直したら usercallback の URI を再確認する

■ 2. invalid_grant

● 発生するケース

  • リフレッシュトークンが期限切れ
  • 認証済みだが権限が変わった
  • OAuth 再認証を行わないまま API を叩いている

● 主な原因

  • Web アプリの権限設定を途中で変えた
  • GCP 側で OAuth クライアントを再作成した
  • トークン保存先(UserProperties)が壊れた

● 対処

  • 一度 GAS 側でトークンをリセット PropertiesService.getUserProperties().deleteAllProperties();
  • 認証 URL を開き直して再度許可する
  • Web アプリの「実行者」「アクセス権限」が正しいか確認

■ 3. insufficient_permissions

● 発生するケース

  • 必要な Scope が OAuth に設定されていない
  • Search Console 上で「所有権が未確認」のサイトを叩いている
  • Readonly 権限を付けていない

● 原因

  • https://www.googleapis.com/auth/webmasters.readonly が設定されていない
  • GSC 側で対象サイトの「所有権確認」が終わっていない

● 対処

  • スコープが正しいかをチェック
  • GSC の設定で、対象 URL が「確認済みプロパティ」になっているかチェック

■ 4. 403 Forbidden(エンドポイントを叩けない)

● 発生するケース

  • 所有者権限がない
  • API の body に誤りがある
  • URL に含めるサイト URL が Search Console の URL と一致していない

● よくある間違い

https://example.com

sc-domain:example.com

として登録しているケース(またはその逆)

● 対処

  • Search Console の「プロパティの URL」を正確に使う
  • sc-domain プロパティか URL-prefix かを統一する
  • dimensions や日付の JSON が API 仕様に合っているか確認

■ 5. トークン期限切れ(silent なエラー)

● 発生する症状

  • fetch が 200 を返しているのにデータが無い
  • 毎日叩いていると突然データが取れない日が出る

● 原因

  • access_type=offlineprompt=consent を設定していない
  • リフレッシュトークンが取得できていない

● 対処

サービス作成時に以下が入っているか再確認:

.setParam('access_type', 'offline')
.setParam('prompt', 'consent')

ZIDOOKA!

この記事の内容について、対応できます

この記事に関連する技術トラブルや開発上の問題について個別対応を行っています。

個別対応は3,000円〜 内容・工数により事前にお見積りします
最後までお読みいただきありがとうございました

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

AI活用に関するポリシー

当サイトでは、記事の執筆補助にAIを活用する場合がありますが、全面的な委任は行いません。