45395 - シコウサクゴ -

git pushで新記事を自動インデックス申請する(Google Indexing API + GitHub Actions)

2026-03-06
インフラ
Google Search Console
Indexing API
GitHub Actions
CI-CD
Node.js
SEO
自動化
Last updated:2026-03-06
8 Minutes
1480 Words

ブログに新記事を書いたあと、Google Search Console を開いて URL を貼り付けてインデックス申請する——これを毎回やっていました。

デプロイは git push で GitHub Actions が自動化しているのに、インデックス申請だけ手動というのが気になっていました。 調べたところ Google Indexing API を使えば同じ CI パイプラインの中に組み込めることがわかったので実装しました。

対象読者

  • 静的ブログを GitHub Actions で自動デプロイしているエンジニア
  • Search Console のインデックス申請を自動化したいひと
  • Node.js で Google API を使う方法を知りたいひと

前提条件

環境バージョン
Node.js>= 18
google-auth-library^9.0.0
GitHub Actions使用中であること

Google Indexing API とは

Google が提供する REST API で、指定した URL のクロール・インデックスを Google に通知できます。

1
POST https://indexing.googleapis.com/v3/urlNotifications:publish

リクエストボディ:

1
{
2
"url": "https://your-site.com/blog/new-post/",
3
"type": "URL_UPDATED"
4
}

type には URL_UPDATED(新規・更新)と URL_DELETED(削除)があります。

公式の対象コンテンツと実態

公式ドキュメントでは JobPosting・BroadcastEvent の構造化データを持つページ向けと記載されています。ただし一般ページにも API 自体は動作します(利用規約上のグレーゾーンである点はご認識ください)。

制限: 1日あたり 200 リクエスト(無料)


セットアップ

1. Web Search Indexing API を有効化

Google Cloud Console でプロジェクトを開き、「API とサービス」→「ライブラリ」 で “Web Search Indexing API” を検索して有効化します。

2. サービスアカウントを GSC のオーナーに追加

Google Search Console に Search Console が認識するサービスアカウントのメールアドレスを オーナー として追加します(「ユーザー」では申請できません)。

  1. Search Console → 設定 → ユーザーと権限
  2. 「ユーザーを追加」→ サービスアカウントのメールアドレスを入力
  3. 権限: 「オーナー」 を選択

3. GitHub Secrets に登録

リポジトリの Settings → Secrets and variables → Actions で追加:

NameValue
GSC_INDEXING_SERVICE_ACCOUNTサービスアカウント JSON の中身をそのままペースト

実装

スクリプト: scripts/request-indexing.mjs

1
import { GoogleAuth } from 'google-auth-library';
2
import { execSync } from 'child_process';
3
4
const SITE_URL = process.env.SITE_URL ?? 'https://45395.jp';
5
const BLOG_CONTENT_DIR = 'src/content/blog/';
6
const INDEXING_API_ENDPOINT = 'https://indexing.googleapis.com/v3/urlNotifications:publish';
7
const DRY_RUN = process.env.DRY_RUN === 'true';
8
9
// git diff で変更されたブログ記事ファイルを取得し、公開 URL に変換する
10
function getChangedBlogUrls() {
11
let diffOutput;
12
try {
13
diffOutput = execSync('git diff --name-only HEAD~1 HEAD', { encoding: 'utf-8' });
14
} catch {
15
// HEAD~1 が存在しない場合(初回コミットなど)はスキップ
77 collapsed lines
16
console.log('git diff に失敗しました。スキップします。');
17
return [];
18
}
19
20
return diffOutput
21
.split('\n')
22
.map(f => f.trim())
23
.filter(f => f.startsWith(BLOG_CONTENT_DIR) && f.endsWith('.md'))
24
.map(f => {
25
const slug = f.replace(BLOG_CONTENT_DIR, '').replace(/\.md$/, '');
26
return `${SITE_URL}/blog/${slug}/`;
27
});
28
}
29
30
async function submitUrl(client, url) {
31
const response = await client.request({
32
url: INDEXING_API_ENDPOINT,
33
method: 'POST',
34
data: { url, type: 'URL_UPDATED' },
35
});
36
return response.data;
37
}
38
39
async function main() {
40
const serviceAccountJson = process.env.GSC_INDEXING_SERVICE_ACCOUNT;
41
if (!serviceAccountJson) {
42
console.log('GSC_INDEXING_SERVICE_ACCOUNT が未設定のためスキップします。');
43
process.exit(0);
44
}
45
46
const urls = getChangedBlogUrls();
47
if (urls.length === 0) {
48
console.log('変更されたブログ記事はありません。');
49
process.exit(0);
50
}
51
52
console.log(`インデックス申請対象: ${urls.length} 件`);
53
urls.forEach(url => console.log(` ${url}`));
54
55
if (DRY_RUN) {
56
console.log('\n[DRY RUN] 申請はスキップされました。');
57
process.exit(0);
58
}
59
60
const auth = new GoogleAuth({
61
credentials: JSON.parse(serviceAccountJson),
62
scopes: ['https://www.googleapis.com/auth/indexing'],
63
});
64
const client = await auth.getClient();
65
66
let successCount = 0;
67
let failCount = 0;
68
69
for (const url of urls) {
70
try {
71
const result = await submitUrl(client, url);
72
console.log(`✓ ${url} (notifyTime: ${result.urlNotificationMetadata?.latestUpdate?.notifyTime ?? '-'})`);
73
successCount++;
74
} catch (err) {
75
const status = err.response?.status ?? 'unknown';
76
const message = err.response?.data?.error?.message ?? err.message;
77
console.error(`✗ ${url} [HTTP ${status}]: ${message}`);
78
failCount++;
79
}
80
}
81
82
console.log(`\n完了: 成功 ${successCount} 件 / 失敗 ${failCount} 件`);
83
84
if (successCount === 0 && failCount > 0) {
85
process.exit(1);
86
}
87
}
88
89
main().catch(err => {
90
console.error('予期しないエラー:', err);
91
process.exit(1);
92
});

ポイントを解説します。

URL の自動検出

1
execSync('git diff --name-only HEAD~1 HEAD', { encoding: 'utf-8' })

git diff --name-only HEAD~1 HEAD で直前のコミットとの差分ファイル一覧を取得します。src/content/blog/ 配下の .md ファイルに絞り込み、URL に変換します。

1
src/content/blog/gsc-indexing-api.md
2
→ https://45395.jp/blog/gsc-indexing-api/

サービスアカウント認証

google-auth-libraryGoogleAuth クラスに認証情報と使用するスコープを渡すだけです。

1
const auth = new GoogleAuth({
2
credentials: JSON.parse(serviceAccountJson),
3
scopes: ['https://www.googleapis.com/auth/indexing'],
4
});
5
const client = await auth.getClient();

あとは client.request() で API を叩けます。ライブラリがトークン取得・リフレッシュを自動で処理してくれます。

エラーハンドリングの方針

インデックス申請の失敗でデプロイを止めたくないので:

  • URL ごとにエラーをキャッチして続行
  • 全件失敗した場合のみ exit(1) で GitHub Actions に失敗を通知
  • GitHub Actions 側でも continue-on-error: true を設定
1
if (successCount === 0 && failCount > 0) {
2
process.exit(1);
3
}

DRY RUN モード

DRY_RUN=true を設定すると申請せずに対象 URL を確認できます。ローカルでの動作確認に使います。

Terminal window
1
DRY_RUN=true GSC_INDEXING_SERVICE_ACCOUNT='{}' node scripts/request-indexing.mjs
2
# インデックス申請対象: 1 件
3
# https://45395.jp/blog/gsc-indexing-api/
4
# [DRY RUN] 申請はスキップされました。

package.json への追加

1
{
2
"devDependencies": {
3
"google-auth-library": "^9.0.0"
4
}
5
}

GitHub Actions への組み込み

デプロイステップの直後に追加します。

.github/workflows/firebase-hosting-merge.yml
1
- name: Request Google Search indexing
2
continue-on-error: true
3
env:
4
GSC_INDEXING_SERVICE_ACCOUNT: ${{ secrets.GSC_INDEXING_SERVICE_ACCOUNT }}
5
run: node scripts/request-indexing.mjs

continue-on-error: true により、インデックス申請が失敗してもデプロイ自体は成功扱いになります。


動作フロー

1
git push origin main
2
3
4
GitHub Actions
5
├─ pnpm install && pnpm build
6
├─ firebase deploy → Firebase Hosting に公開
7
└─ node request-indexing.mjs
8
9
├─ git diff で変更した .md ファイルを検出
10
├─ URL に変換
11
└─ Google Indexing API に POST
12
→ Google が数時間〜数日でクロール・インデックス

注意点

API の制限

項目上限
1日あたりのリクエスト数200 件
バースト上限600 件(短時間)

1日に大量記事を公開しない個人ブログなら十分です。

Sitemap Ping との併用

今回の実装に加え、サイトマップの更新を Google に通知する “Sitemap Ping” も組み合わせると効果的です。

1
- name: Ping Google sitemap
2
run: curl "https://www.google.com/ping?sitemap=https://45395.jp/sitemap-index.xml"

こちらは API キー不要で、1行追加するだけです。

インデックスされるタイミング

Indexing API はあくまで「クロールのリクエスト」であり、即時インデックスを保証するものではありません。実際には数時間〜数日でインデックスされることが多いです。


まとめ

実装に必要だったのは以下だけでした:

  1. Google Cloud で Web Search Indexing API を有効化
  2. サービスアカウントを GSC にオーナーとして登録
  3. GitHub Secret に JSON を登録
  4. スクリプト 1 ファイル + ワークフロー 5 行

git push でデプロイとインデックス申請が一度に完了するようになり、手動作業がひとつ減りました。同じ構成のかたの参考になれば幸いです。

Article title:git pushで新記事を自動インデックス申請する(Google Indexing API + GitHub Actions)
Article author:45395
Release time:2026-03-06