🧑‍💻 開発者向けClaude Code × GA4・Search Console API

Claude CodeでGA4・Search Console APIをつなぎ、サイト分析とSEO改善を自動化する【2026】データ取得から改善PRまで

「GA4とSearch Consoleを毎週見て、CTRの低いページのtitleを直す」——大事だと分かっていても後回しになりがちな作業です。この記事では、Claude Code(ターミナルで動くAnthropicのコーディングエージェント)とGA4 Data API・Search Console APIをつなぎ、データ取得 → 改善候補の抽出 → HTMLの修正案 → ブランチとPR作成までを任せる構成を、動くコードと設定ファイル付きで解説します。

🔬
調査・編集:AIエージェント研究所 編集部
運営=広告運用・生成AIエージェント導入支援の現役コンサル。このサイト自体も、AIエージェントと一緒に作り・公開・運用しています。

API仕様・料金は執筆時点(2026年9月)の情報です。利用前に必ず各公式ドキュメントをご確認ください。→ 運営者・編集方針について

公開:2026年9月26日/最終更新:2026年9月26日
この記事の結論(要点まとめ)
  • データ取得と集計はPythonスクリプトで決定的に行い、Claude Codeには解釈・修正案・コード変更を任せる。役割を分けると数字がブレない
  • GA4はサービスアカウントをプロパティの閲覧者に、Search Consoleはサービスアカウントをユーザーとして追加。スコープは読み取り専用で十分
  • CLAUDE.mdに「データの取り方」「直し方のルール」を書き、.claude/settings.jsonのpermissionsで実行してよいコマンドを絞る
  • 静的サイト×Gitなら、分析 → HTML修正 → PR → 人がレビューしてマージまで一本の流れにできる。最後の判断は人が持つ

全体像:何をスクリプトに、何をClaude Codeに任せるか

最初に設計の結論を書きます。Claude CodeにAPIを直接叩かせて、返ってきたJSONを読んで分析させる——これは一見スマートですが、本番運用ではおすすめしません。理由は3つあります。

  • 数字が再現しない:同じデータでも、LLMに集計や並べ替えをさせると毎回わずかに違う結果になり得ます。「先週と比べてCTRが下がった」という判断の土台が揺らぎます
  • トークンの無駄:Search Consoleのpage×queryデータは、中規模サイトでも数万行になります。全部を会話に流し込むのはコストも時間もかかります
  • 権限が広がりすぎる:APIを自由に叩ける状態は、認証情報を自由に読める状態とほぼ同じです

そこで、次のように役割を分けます。

GA4Search Console① 取得fetch_ga4/gsc.pyスクリプト② 抽出find_low_ctr.pyスクリプト③ 解釈・修正title等を直すClaude CodeデータCSV候補PR④ 判断PRをレビュー人サイトHTMLファイルマージHTMLを読む公開後の効果を測る
① fetch_ga4.py/fetch_gsc.pyがAPIからCSVを作り、② find_low_ctr.pyが条件で候補ページを絞ります(ここまで決定的なスクリプト)。③ Claude CodeがHTMLを読んで検索意図とのずれを判断し、title等を直してPRに。④ 人がレビューしてマージすると公開されます。

ポイントは、Claude Codeが「スクリプトを実行する係」と「文章とHTMLを直す係」を兼ねることです。スクリプト自体もClaude Codeに書かせて構いません。ただし、一度書いて人がレビューしたスクリプトを固定の道具として使い回し、毎回その場でAPIコードを書かせない。これが数字の再現性と安全性を両立させるコツです。

助手アイ
助手アイAIに全部任せるほうが楽そうですけど、あえて「スクリプト」と「AI」に分けるんですね。
エージェント博士
エージェント博士電卓で済む計算を名探偵にやらせる必要はないんじゃ。集計は電卓(スクリプト)、推理は探偵(Claude)。こうしておくと、結論がおかしいときに「数字が違うのか、解釈が違うのか」を切り分けられるんじゃぞ

準備:GCP・サービスアカウント・GA4とSearch Consoleの権限

GA4 Data APIとSearch Console APIは、どちらもGoogle Cloudのプロジェクト経由で使います。定期実行を前提にするなら、個人のGoogleアカウントでOAuthするよりサービスアカウント(人ではなくプログラム用のアカウント)が扱いやすいです。

  1. Google Cloudでプロジェクトを作り、Google Analytics Data APIとGoogle Search Console APIを有効化する
  2. IAMでサービスアカウントを作成し、JSON鍵を発行する(組織のポリシーで鍵の発行が禁止されている場合は、Workload Identity連携や gcloud auth application-default login によるADCなど、鍵を持たない方法を検討)
  3. GA4:管理 → プロパティのアクセス管理で、サービスアカウントのメールアドレスを閲覧者として追加
  4. Search Console:設定 → ユーザーと権限で、サービスアカウントのメールアドレスをユーザーとして追加(データの閲覧が目的なので、フル権限は不要。権限の名称と範囲は執筆時点のもの。公式ヘルプで確認を)
  5. ライブラリをインストール:pip install google-analytics-data google-api-python-client google-auth
項目GA4 Data APISearch Console API
分かることサイト内の行動(セッション、エンゲージメント、キーイベント)Google検索での露出(表示回数、クリック、CTR、平均掲載順位)
Pythonライブラリgoogle-analytics-datagoogle-api-python-client(build("searchconsole", "v1"))
主なメソッドrun_reportsearchanalytics().query()
権限の付け方プロパティにサービスアカウントを「閲覧者」で追加プロパティのユーザーにサービスアカウントを追加
対象の指定properties/数字のプロパティIDsc-domain:example.com または URLプレフィックス

鍵ファイルはリポジトリの外か、少なくとも.gitignoreとClaude Codeのdenyの両方で守られた場所に置きます。パスは環境変数 GOOGLE_APPLICATION_CREDENTIALS で渡します。

.gitignoretext
# .gitignore(抜粋)
.env
secrets/
*.key
credentials*.json
reports/
logs/
鍵ファイルを「Gitに入れない」だけでは足りない .gitignoreはGitの話で、Claude Codeがファイルを読めるかどうかとは別です。作業ディレクトリ内に鍵があれば、エージェントはそれを読めてしまいます。後述のpermissions.denyでReadを禁止するのが二重の防御になります。

GA4 Data APIの取得スクリプト(run_report)

ページ別に、セッション数・エンゲージメント率・キーイベント数を取得してCSVに保存するスクリプトです。BetaAnalyticsDataClient()は引数なしで生成すると、環境変数GOOGLE_APPLICATION_CREDENTIALSが指す認証情報を使います。

scripts/fetch_ga4.pyPython
# scripts/fetch_ga4.py — GA4 Data API でページ別の指標を取得して CSV に保存
import csv
import os
import sys

from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
    DateRange,
    Dimension,
    Metric,
    RunReportRequest,
)

PROPERTY_ID = os.environ["GA4_PROPERTY_ID"]  # 例: 123456789(数字のみ)
OUT = "reports/ga4_pages.csv"


def main(days: int = 28) -> None:
    # 認証は環境変数 GOOGLE_APPLICATION_CREDENTIALS のサービスアカウント鍵を使う
    client = BetaAnalyticsDataClient()
    request = RunReportRequest(
        property=f"properties/{PROPERTY_ID}",
        dimensions=[Dimension(name="pagePath")],
        metrics=[
            Metric(name="sessions"),
            Metric(name="engagementRate"),
            Metric(name="keyEvents"),
        ],
        date_ranges=[DateRange(start_date=f"{days}daysAgo", end_date="yesterday")],
        limit=10000,
    )
    response = client.run_report(request)

    os.makedirs("reports", exist_ok=True)
    with open(OUT, "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["page_path", "sessions", "engagement_rate", "key_events"])
        for row in response.rows:
            w.writerow([row.dimension_values[0].value]
                       + [m.value for m in row.metric_values])
    print(f"{len(response.rows)} rows -> {OUT} (total rows: {response.row_count})")


if __name__ == "__main__":
    main(int(sys.argv[1]) if len(sys.argv) > 1 else 28)

設計上のポイントをいくつか補足します。

  • 終了日をyesterdayにする:当日のデータは集計途中で、見るたびに数字が変わります。比較の土台にするなら確定した日までに揃えるのが安全です
  • ディメンションは必要最小限:ディメンションを増やすほど行数が増え、行数の上限や「(other)」への集約に当たりやすくなります。「何を判断するためのデータか」から逆算して選びます
  • row_countを出力する:取得した行数と全体の行数が違えば、limitやoffsetでページングが必要だと分かります
  • 指標名(keyEventsなど)はGA4の仕様変更で名称が変わることがあります。執筆時点の名称なので、公式のAPIスキーマ(Dimensions & Metrics)で確認してください

Search Console APIの取得スクリプト(searchanalytics.query)

SEO改善で本当に効くのは、GA4よりもSearch Consoleの「検索結果での見え方」のデータです。page×query(どのページが、どの検索語句で、何回表示され、何回クリックされたか)を取得します。

scripts/fetch_gsc.pyPython
# scripts/fetch_gsc.py — Search Console API で page × query の実績を取得
import csv
import datetime as dt
import os
import sys

from google.oauth2 import service_account
from googleapiclient.discovery import build

SITE_URL = os.environ["GSC_SITE_URL"]  # 例: "sc-domain:example.com" または "https://example.com/"
KEY_PATH = os.environ["GOOGLE_APPLICATION_CREDENTIALS"]
SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"]  # 読み取り専用スコープ
OUT = "reports/gsc_page_query.csv"


def main(days: int = 28) -> None:
    creds = service_account.Credentials.from_service_account_file(KEY_PATH, scopes=SCOPES)
    service = build("searchconsole", "v1", credentials=creds)

    # 直近数日はデータが確定していないことがあるので、終了日を少し手前にずらす
    end = dt.date.today() - dt.timedelta(days=3)
    start = end - dt.timedelta(days=days - 1)

    rows, start_row = [], 0
    while True:
        body = {
            "startDate": start.isoformat(),
            "endDate": end.isoformat(),
            "dimensions": ["page", "query"],
            "rowLimit": 25000,
            "startRow": start_row,
        }
        resp = service.searchanalytics().query(siteUrl=SITE_URL, body=body).execute()
        batch = resp.get("rows", [])
        rows += batch
        if len(batch) < 25000:
            break
        start_row += 25000

    os.makedirs("reports", exist_ok=True)
    with open(OUT, "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["page", "query", "clicks", "impressions", "ctr", "position"])
        for r in rows:
            page, query = r["keys"]
            w.writerow([page, query, r["clicks"], r["impressions"],
                        round(r["ctr"], 4), round(r["position"], 1)])
    print(f"{len(rows)} rows ({start} - {end}) -> {OUT}")


if __name__ == "__main__":
    main(int(sys.argv[1]) if len(sys.argv) > 1 else 28)
  • スコープはwebmasters.readonly:分析だけなら読み取り専用で足ります。権限は「必要最小限」から始め、足りなくなったら理由を添えて広げるのが原則です
  • 終了日を3日前にずらす:Search Consoleのデータは反映まで数日かかることがあり、直近は数字が確定していない場合があります
  • 1回のリクエストは最大25,000行(執筆時点)。startRowでページングします
  • page×queryの合計は、ページ単位の合計と一致しない:プライバシー保護のため、ごく少数の人しか検索していないクエリはデータから省かれます。「クエリ別の内訳の合計<ページ全体」は仕様です
基礎から学びたい方へ API・JSON・HTTP・環境変数といった前提知識があいまいな場合は、AIエージェント時代のIT基礎ロードマップ(全7章)から読むと、この記事のコードがぐっと読みやすくなります。

改善候補を決定的に抽出する

「表示回数は多いのにCTRが低いページ」は、title・descriptionの改善余地が大きい典型です。ただ、この抽出条件をClaudeに毎回言葉で考えさせると、閾値が毎回変わります。条件はコードに固定し、Claudeには結果の解釈を任せます。

scripts/find_low_ctr.pyPython
# scripts/find_low_ctr.py — 「表示は多いがCTRが低いページ」を決定的に抽出する
import csv
from collections import defaultdict

MIN_IMPRESSIONS = 500  # 判断に足る表示回数(サイト規模に合わせて調整)
MAX_POSITION = 15.0  # 2ページ目前半まで。圏外のページはtitle改善より先に内容の見直し


def main() -> None:
    pages = defaultdict(lambda: {"clicks": 0, "impressions": 0, "pos_x_imp": 0.0, "queries": []})
    with open("reports/gsc_page_query.csv", encoding="utf-8") as f:
        for r in csv.DictReader(f):
            p = pages[r["page"]]
            imp = int(r["impressions"])
            p["clicks"] += int(r["clicks"])
            p["impressions"] += imp
            p["pos_x_imp"] += float(r["position"]) * imp
            p["queries"].append((imp, r["query"]))

    out = []
    for url, p in pages.items():
        if p["impressions"] < MIN_IMPRESSIONS:
            continue
        ctr = p["clicks"] / p["impressions"]
        pos = p["pos_x_imp"] / p["impressions"]  # 表示回数で重み付けした平均掲載順位
        if pos > MAX_POSITION:
            continue
        top_q = [q for _, q in sorted(p["queries"], reverse=True)[:5]]
        out.append([url, p["impressions"], p["clicks"], round(ctr, 4), round(pos, 1), " / ".join(top_q)])

    out.sort(key=lambda x: (x[3], -x[1]))  # CTRが低く、表示が多い順
    with open("reports/low_ctr_pages.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["page", "impressions", "clicks", "ctr", "avg_position", "top_queries"])
        w.writerows(out[:30])
    print(f"{len(out)} candidates -> reports/low_ctr_pages.csv (top 30)")


if __name__ == "__main__":
    main()

ここでの判断基準(トレードオフ)は次のとおりです。

パラメータ小さく/緩くすると大きく/厳しくすると
MIN_IMPRESSIONS候補が増えるが、少ない表示回数のCTRはブレが大きく、偶然の上下に振り回される判断は安定するが、小規模サイトでは候補がほとんど出ない
MAX_POSITION上位表示のページに絞れる(title改善が効きやすい)圏外のページまで含むが、そもそも見られていないのでtitleだけ直しても効果は薄い
比較の軸CTRの絶対値だけで比べると、順位が低いページが常に上位に来る順位帯ごとのサイト内平均CTRと比べると公平だが、ロジックが複雑になる

最初はこのシンプルな版で回し、慣れてきたら「掲載順位帯ごとのサイト内平均CTRより低いページ」を抽出するように改良すると精度が上がります。この改良自体をClaude Codeに頼み、差分をレビューしてから採用するのがよい使い方です。

CLAUDE.mdとpermissionsで「やってよいこと」を決める

Claude Codeは、プロジェクト直下のCLAUDE.mdを起動のたびに読み込みます。いわば「このリポジトリの手順書」です(/initで雛形を作れます)。ここにデータの取り方と直し方のルールを書いておくと、毎回プロンプトで説明する必要がなくなり、担当者が変わっても同じ品質で回ります。

CLAUDE.mdMarkdown
# CLAUDE.md(サイト分析・SEO改善用)

## このリポジトリについて
- 静的HTMLサイト。記事は直下の *.html。共通CSSは assets/style.css
- 公開は main ブランチへのマージで自動デプロイされる。main に直接コミットしない

## データの取り方(必ずスクリプト経由)
- GA4: `python scripts/fetch_ga4.py 28` → reports/ga4_pages.csv
- Search Console: `python scripts/fetch_gsc.py 28` → reports/gsc_page_query.csv
- 改善候補: `python scripts/find_low_ctr.py` → reports/low_ctr_pages.csv
- 数値の集計・比較はスクリプトの出力を使う。CSVを目で読んで暗算しない

## title / description を直すときのルール
- title は32字前後を目安に、主要クエリを前半に入れる。「【2026】」表記と「 - サイト名」は残す
- description は90〜120字。本文に書いていないことを約束しない(誇大表現・断定の禁止)
- 1回の作業で直すのは最大5ページ。作業ブランチ `seo/YYYYMMDD` を切り、変更理由を PR 本文に表で書く
- 数値(表示回数・CTR・順位)を PR に書くときは reports/ の値をそのまま引用する

## 触ってはいけないもの
- .env、secrets/ 配下、サービスアカウントの鍵ファイル
- 計測タグ(GTM・AdSense)のスクリプト

次に、.claude/settings.jsonのpermissionsで、承認なしで実行してよいコマンドと絶対にさせない操作を決めます。allowは確認なしで実行、askは毎回確認、denyは禁止です。

.claude/settings.jsonJSON
{
  "permissions": {
    "allow": [
      "Bash(python scripts/fetch_ga4.py:*)",
      "Bash(python scripts/fetch_gsc.py:*)",
      "Bash(python scripts/find_low_ctr.py:*)",
      "Bash(git status:*)",
      "Bash(git diff:*)",
      "Read(./reports/**)"
    ],
    "ask": [
      "Bash(git push:*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./secrets/**)",
      "Bash(curl:*)"
    ]
  }
}
  • allowは「取得・抽出スクリプト」と「git status / diff」だけ:毎回確認を求められると運用が回らないので、安全と分かっている読み取り系だけを許可します
  • pushはask:ブランチへのpushは人がひと目確認してから。mainへの直接pushは、GitHub側のブランチ保護でも止めておくと二重になります
  • .envと鍵はdeny:スクリプトが環境変数経由で使うので、Claude自身が中身を読む必要はありません
  • curlはdeny:任意のHTTPリクエストを許すと、取得スクリプトを迂回してAPIを叩いたり、データを外部に送ったりできてしまいます
エージェント博士
エージェント博士CLAUDE.mdは「お願い」、permissionsは「鍵」じゃ。CLAUDE.mdに「.envを読むな」と書いても、それはお願いにすぎん。守らせたいことは設定で強制、判断の指針はCLAUDE.mdに書く。この使い分けが大事なんじゃぞ

Claude Codeとの対話例:CTR改善PRができるまで

準備ができたら、リポジトリのディレクトリでclaudeを起動し、次のように頼みます。以下は流れのイメージです(実際の出力は毎回異なります)。

ターミナル(イメージ)text
> 直近28日で、表示回数は多いのにCTRが低いページを抽出して、
  title と description の改善案を出して。修正はブランチを切ってPRまで。

● Bash(python scripts/fetch_gsc.py 28)
  ⎿ 18,442 rows (2026-08-27 - 2026-09-23) -> reports/gsc_page_query.csv
● Bash(python scripts/find_low_ctr.py)
  ⎿ 12 candidates -> reports/low_ctr_pages.csv (top 30)
● Read(reports/low_ctr_pages.csv)
● Read(example-guide.html)

  候補の上位3ページを確認しました。例えば example-guide.html は
  平均順位は上位なのに、上位クエリ「〇〇 やり方」「〇〇 初心者」に対して
  title が「〇〇の歴史と背景」寄りで、検索意図とずれている可能性があります。
  (以下、ページごとに 現状 / 改善案 / 理由 の表)

● Bash(git switch -c seo/20260926)
● Edit(example-guide.html)
● Bash(git push -u origin seo/20260926)   ← ask 設定のため確認を求められる

この流れで、人がやることは最初の一言と、最後のPRレビューだけです。レビューでは次の観点を見ます。

  • 数字の引用が正しいか:PR本文の表示回数・CTRがreports/のCSVと一致しているか
  • 本文と約束がずれていないか:titleで「完全ガイド」「最安」とうたっているのに、本文がそうなっていない——は、CTRが上がっても直帰が増えるだけです
  • 検索意図の読み違い:上位クエリを見て「初心者向けの手順」を求められていると判断したのは妥当か。ここは運用者の現場感が一番効くところです
  • サイト全体の一貫性:同じテーマの別ページとtitleが似すぎて、共食い(カニバリゼーション)を起こさないか
修正案の出させ方のコツ 「改善して」ではなく、「現状 / 改善案 / 理由 / 根拠にした上位クエリ」の表で出してと形式を指定すると、レビューが格段に楽になります。形式はCLAUDE.mdやSkillに書いておけば毎回指定しなくて済みます。

静的サイト×Gitだと「分析→修正→PR」が一気通貫になる

この仕組みが特に相性がいいのは、記事がHTMLファイル(やMarkdown)としてGitリポジトリに入っているサイトです。CMSのデータベースに記事がある場合、Claude Codeが修正を「提案」はできても、反映にはCMSの管理画面やAPIが別途必要になります。一方、静的サイトなら記事はただのファイルなので、

  • Claude Codeがファイルを直接読んで、検索意図と本文のずれを確認できる
  • 修正は差分(diff)として残り、PRでレビューできる
  • マージすればデプロイが走り、問題があればrevertで即座に戻せる
  • 「いつ・なぜ・誰の承認で」titleを変えたかが、Gitの履歴にそのまま残る。効果測定のときに変更日を正確にたどれる
自社事例

このサイトの構成なら、どうつなげられるか

当サイト(AIエージェント研究所)は、HTMLとCSSだけの静的サイトをGitHubでGit管理し、Cloudflare Pagesで配信しています。記事の追加や修正は、AIエージェント(Claude Code)が作業ブランチで変更し、PRを人が確認してマージする流れです。公開前には、共通フッターを全ページに流し込むNode.jsスクリプト(フッターを入れられないページがあるとエラーで止まる)や、広告表記まわりを機械チェックするスクリプトも実行しています。

この「ブランチ → 機械チェック → 人のレビュー → マージで公開」という土台がすでにあるので、この記事のGA4・Search Console連携を足せば、分析からPRまでを同じ流れに乗せられる構成になっています。改善PRも他の変更と同じチェックを通るため、「SEOのためにtitleを変えたら表記ルールが崩れた」といった事故も仕組みで防げます。

当社が支援の現場で採る考え方も同じで、数値の取得・集計はスクリプト、解釈と修正案はAI、公開の判断は人という分担を最初に決めます。サイト運用の仕組み全体はAIエージェントでメディアサイトを運用する仕組みで詳しく紹介しています。

Skills化と定期実行(ヘッドレス)

Skills:定型業務を「手順パッケージ」にする

毎週同じ手順を踏むなら、Skillsにまとめます。.claude/skills/<name>/SKILL.mdに、フロントマター(nameとdescription)と手順を書いておくと、Claude Codeは関連する依頼を受けたときにこの手順を読み込んで使います。descriptionは「いつ使うか」が伝わるように具体的に書くのがコツです。

.claude/skills/seo-ctr-review/SKILL.mdMarkdown
---
name: seo-ctr-review
description: Search Console のデータから、表示回数が多くCTRが低いページを抽出し、title/descriptionの改善PRを作る。SEO改善、CTR改善、タイトル見直しを頼まれたときに使う。
---

# CTR改善レビューの手順

1. `python scripts/fetch_gsc.py 28` と `python scripts/find_low_ctr.py` を実行する
2. reports/low_ctr_pages.csv の上位から最大5ページを選ぶ(順位が15位より下のページは対象外)
3. 各ページのHTMLを読み、上位クエリと title / h1 / description のずれを確認する
4. CLAUDE.md の「title / description を直すときのルール」に従って修正案を作る
5. 作業ブランチ `seo/YYYYMMDD` で修正し、PR本文に次の表を付ける
   | ページ | 表示回数 | CTR | 平均順位 | 旧title | 新title | 変更理由 |
6. 前回のPRから4週間以上たったページは、変更前後のCTRを比較してPRに追記する

CLAUDE.mdとの使い分けは、CLAUDE.md=いつでも守るルール、Skill=特定の作業のときだけ使う手順です。CLAUDE.mdに作業手順を全部書くと、関係ない作業のときにも毎回読み込まれて長くなるので、手順はSkillに逃がしておくと見通しがよくなります。

ヘッドレス実行:cronやCIから週1回まわす

claude -p "プロンプト"で非対話(ヘッドレス)実行ができます。--allowedToolsで、その実行に限って使ってよいツールを明示します。対話時と違って途中で人が確認できないので、許可するツールは対話時より狭くするのが原則です。

scripts/weekly_seo.shBash
#!/usr/bin/env bash
# scripts/weekly_seo.sh — 週1回、非対話モードで改善PRを作る(cron等から実行)
set -euo pipefail
cd "$(dirname "$0")/.."
git switch main && git pull --ff-only

claude -p "seo-ctr-review の手順で今週のCTR改善PRを作成して。対象は最大3ページ。" \
  --allowedTools "Bash(python scripts/fetch_gsc.py:*)" "Bash(python scripts/find_low_ctr.py:*)" \
                 "Read" "Edit" "Bash(git switch:*)" "Bash(git add:*)" "Bash(git commit:*)" \
                 "Bash(git push:*)" "Bash(gh pr create:*)" \
  --output-format json > "logs/seo_$(date +%Y%m%d).json"

このスクリプトをcronで週1回動かすか、GitHub Actions(Anthropicが公開しているanthropics/claude-code-actionなど)から実行します。Actionsで動かす場合、APIキーやサービスアカウント鍵はリポジトリのSecretsに入れ、ワークフローのログに出力しないよう注意します。--output-format jsonで結果をログとして残しておくと、後から「どの週に何をしたか」を追えます。料金やプラン、CLIのオプションは更新が速いので、執筆時点の情報として公式ドキュメントで確認してください。

助手アイ
助手アイ自動でPRまで作ってくれるなら、マージも自動にしちゃえば完全自動化ですよね?
エージェント博士
エージェント博士そこは急がんほうがいいのう。titleはサイトの「看板」じゃ。作るところまでは自動、公開するかどうかは人。しばらく運用してレビューで直すところがほぼ無くなってから、範囲を広げるかどうか考えればよいんじゃ

注意点:個人情報・しきい値・データの遅れ・効果測定

個人情報とデータの置き場所

  • GA4には個人を特定できる情報を送らないのがGoogleのポリシーです。とはいえ、URLのクエリパラメータにメールアドレスが入ってしまうような実装ミスは現場で起こり得ます。pagePathを取得したら、Claudeに渡す前に怪しいパラメータを除去するのが安全です
  • 検索クエリにも個人名が含まれることがある:人名や社名+トラブル系の語句などです。reports/はGitにコミットせず、共有範囲を決めておきます
  • Claude Codeに渡したデータは、利用しているプラン・契約のデータ取り扱い条件に従って処理されます。社内ルールで外部AIに渡してよいデータの範囲を決めておきましょう

しきい値・サンプリング・行数の上限

  • GA4のデータしきい値:Googleシグナルの設定などによっては、個人の特定を防ぐためにしきい値が適用され、少数の行がレポートから除かれることがあります。管理画面の数字とAPIの数字が合わないときは、まずここを疑います
  • カーディナリティ(値の種類の多さ):ディメンションの値が多すぎると「(other)」にまとめられることがあります
  • Search Consoleの匿名化クエリ:前述のとおり、クエリ別の内訳は全体の一部です
  • APIの割り当て(クォータ):どちらのAPIにも利用上限があります。定期実行を増やすときは、取得範囲を広げすぎないよう注意します(上限値は執筆時点の公式ドキュメントで確認)

データの遅れと効果測定

  • Search Consoleは反映に数日かかるため、title変更の効果は最低でも2〜4週間は様子を見てから判断します
  • Googleが検索結果のtitleを書き換えて表示することがある:HTMLのtitleを変えても、そのまま表示されるとは限りません。descriptionも、検索語句に応じて本文から抜粋されることがよくあります
  • 季節性や順位変動と切り分ける:CTRが上がっても、同時期に順位が上がっていれば効果の一部はそちらです。比較するときは「同じ順位帯でのCTR」を見るようにします。変更日はGitの履歴から正確に取れます
外部のテキストに書かれた「指示」に注意 検索クエリやページの本文は、外部の誰かが書いたテキストです。まれに、AIへの指示のような文字列が紛れ込む可能性があります(プロンプトインジェクション)。この構成では書き込み権限をブランチとPRに限定し、公開は人の判断にしているため、仮に変な提案が出てもレビューで止められます。詳しい対策はAIエージェントを本番運用する安全設計ガイドで解説しています。

よくある質問

Claude CodeにGA4やSearch ConsoleのAPIを直接叩かせてはいけないの?

禁止ではありませんが、本番運用ではおすすめしません。LLMに集計させると結果が毎回わずかに変わり得るうえ、大量の行を会話に流すとコストもかかります。人がレビューした取得・集計スクリプトを固定の道具として用意し、Claude Codeにはそれを実行させて、結果の解釈と修正案づくりを任せるほうが、数字の再現性と安全性を両立できます。

サービスアカウントにはどんな権限を付ければいい?

分析が目的なら読み取りだけで十分です。GA4はプロパティのアクセス管理でサービスアカウントを閲覧者として追加し、Search Consoleはユーザーと権限でサービスアカウントを追加します。APIのスコープもwebmasters.readonlyのような読み取り専用を使います。権限の名称や範囲は変わることがあるため、執筆時点の公式ヘルプで確認してください。

WordPressなどCMSのサイトでも同じことができる?

データ取得と改善候補の抽出、修正案の作成までは同じようにできます。ただし記事がデータベースにあるため、反映にはCMSのAPIや管理画面での作業が別途必要です。記事がファイルとしてGitに入っている静的サイトなら、Claude Codeが直接ファイルを修正してPRにでき、差分レビューやrevertもしやすいので、分析から修正まで一気通貫にしやすくなります。

titleを変えたら、どれくらいで効果を判断できる?

Search Consoleはデータの反映に数日かかるため、最低でも2〜4週間は様子を見るのがおすすめです。また、Googleが検索結果のtitleを書き換えて表示することもあり、順位の変動や季節性の影響も受けます。変更日をGitの履歴で確認し、同じ掲載順位帯のCTRで比較すると判断のブレを減らせます。

まとめ|「取得はスクリプト、判断は人、その間をClaude Code」

GA4 Data APIとSearch Console APIをClaude Codeにつなぐと、これまで「分かってはいるけど手が回らなかった」サイト改善を、毎週の仕組みとして回せるようになります。成功のポイントは、APIを自由に叩かせるのではなく、レビュー済みのスクリプトで数字を決定的に作り、Claude Codeには解釈と修正を任せ、公開は人がPRで判断するという役割分担です。まずは手元でfetch_gsc.pyとfind_low_ctr.pyを動かし、対話でPRを1本作ってみるところから始めてみてください。広告媒体のAPIとつなぐ方法はClaude Codeと広告媒体APIをつなぐ実装ガイド、モデルそのものの違いを知りたい方はChatGPT・Claude・Gemini モデル比較もあわせてどうぞ。

業務自動化のスキルを、キャリアと収入につなげる

APIとAIエージェントで業務を自動化できる人材は、社内でも社外でも求められています。次の一歩に役立つ記事です。

AIで独立するには?未経験から案件を取る6ステップ【2026】自動化・開発スキルを案件にする。フリーランスエージェントの選び方も
記事を読む

次の一歩

姉妹サイトの記事で、次に気になりやすいことを調べられます。