Claude Media
Claude CodeのSearch Console API活用 — 順位を定点観測する

Claude CodeのSearch Console API活用 — 順位を定点観測する

Claude Codeに取得スクリプトを書かせ、Search Console APIのsearchAnalytics.queryで週次のクリックと順位を記録する構成です。権限設定、行数上限、定期実行の選び方まで扱います。

Search Consoleの画面で順位を毎週確認するのは手間がかかります。Search Console APIを使えば、クエリ別のクリック数・表示回数・平均掲載順位をスクリプトで取得できます。そのスクリプトをClaude Codeに書かせ、定期実行に乗せるのが本記事の構成です。

結論から言うと、必要な部品は3つです。読み取り権限を持つサービスアカウント、searchAnalytics.queryを呼ぶ取得スクリプト、そして定期実行の仕組みです。

Search Console APIで取れるものと取れないもの

APIは4つのサービスで構成されます。

サービスできること
Search Analyticsできることサイトの検索トラフィックデータを問い合わせる
Sitemapsできることサイトマップの一覧取得・情報取得・送信
Sitesできることプロパティの一覧・追加・削除
URL InspectionできることGoogleインデックス上のページ状態を調べる

順位の定点観測に使うのは、Search Analyticsのqueryメソッドです。画面のパフォーマンスレポートに近いデータを、日付やクエリで区切って返します。

呼び出し先はPOST /sites/siteUrl/searchAnalytics/queryです。siteUrlはプロパティの種類で書き方が変わります。URLプレフィックスのプロパティならhttps://www.example.com/、ドメインプロパティならsc-domain:example.comです。

サービスアカウントを用意してSearch Consoleに追加する

無人で動かすスクリプトには、ブラウザ操作が要らないサービスアカウントが向いています。手順は次の流れです。

  1. Google Cloudでプロジェクトを作り、Search Console APIを有効にする
  2. サービスアカウントを作成し、JSON鍵を発行する
  3. Search Consoleの「設定」から、そのサービスアカウントのメールアドレスをユーザーに追加する

3番目が見落としやすい点です。APIを有効にしただけでは、サービスアカウントはどのプロパティにも触れません。Search Console側でユーザーとして招待して初めて、データを読めます。

ユーザー追加の入力欄は、Googleアカウントのメールアドレスを受け付けます。公式ヘルプはサービスアカウントに個別には触れていません。サービスアカウントのアドレスを同じ欄に入れる運用は、実務上の定番です。なお、メールグループはユーザーとして追加できません。

ユーザーの種類には、Owner・Full user・Restricted userがあります。スクリプトが読むのはデータだけなので、Ownerを渡す必要はありません。付与する権限は最小のものから試します。

APIを呼ぶときのスコープは、読み取り専用のhttps://www.googleapis.com/auth/webmasters.readonlyで足ります。書き込みを伴うwebmastersスコープは、サイトマップ送信などを使うときだけ選びます。

searchAnalytics.queryのパラメータと上限

リクエスト本文で決めるのは、期間・次元・行数の3点です。

パラメータ内容
startDate / endDate内容必須。YYYY-MM-DD形式でPT時間(UTC-7/8)。両端を含む
dimensions内容query・page・country・device・searchAppearance・dateなど
type内容web(既定)・image・video・news・discover・googleNews
rowLimit内容1〜25,000。既定は1,000
startRow内容0始まりのオフセット。ページ送りに使う
dataState内容省略時は確定データのみ。allで速報値を含む

結果はクリック数の降順で返ります。dateで区切ったときだけ、日付の昇順です。データが無い日は結果から省かれるため、日付の抜けは「ゼロ」ではなく「行が無い」として現れます。

行数の上限は2種類ある

rowLimitの25,000は1回のレスポンスの上限です。全件を取るには、startRowを25,000ずつ増やして、0行が返るまで繰り返します。

もう1つは、データ側の上限です。Search Analyticsが公開するのは、1日・検索タイプごとに最大50K行(クリック順)までです。クエリ×ページの細かい粒度では、長い裾野の一部が取れません。

呼び出し回数の上限は負荷で決まる

1サイトあたり1,200QPMというクォータが定められています。ただ、実際に当たりやすいのは負荷のクォータです。負荷を左右するのは次の点です。

  • ページとクエリの両方で区切る問い合わせが、最も重い
  • 期間が長いほど負荷が増える
  • 同じデータの取り直しは避ける

「毎週、過去6か月分を全部取り直す」設計は避けます。前回以降の日付だけを取り、手元に蓄積する形が向いています。超過したら15分待ち、それでも出るなら日次の長期クォータを超えています。

Claude Codeに取得スクリプトを書かせる

ここからがClaude Codeの出番です。仕様を渡して、蓄積まで含めて書かせます。依頼文の例を挙げます。

Search Console APIのsearchAnalytics.queryで、直近7日のクエリ別クリック・
表示・平均順位を取得するPythonスクリプトを書いて。
- 認証はサービスアカウントのJSON鍵(パスは環境変数SC_KEY_FILE)
- スコープはwebmasters.readonly、siteUrlは環境変数SC_SITE
- rowLimit 25000でstartRowをページ送りし、0行で止める
- 結果をdata/gsc/YYYY-MM-DD.csvに保存し、既存日はスキップ

出てくるスクリプトの骨格は、次のような形になります(例示であり、実行前にレビューしてください)。

import os
from google.oauth2 import service_account
from googleapiclient.discovery import build
 
creds = service_account.Credentials.from_service_account_file(
    os.environ["SC_KEY_FILE"],
    scopes=["https://www.googleapis.com/auth/webmasters.readonly"],
)
svc = build("searchconsole", "v1", credentials=creds)
 
def fetch(day, dimensions=("query",)):
    rows, start = [], 0
    while True:
        body = {
            "startDate": day, "endDate": day,
            "dimensions": list(dimensions),
            "rowLimit": 25000, "startRow": start,
        }
        res = svc.searchanalytics().query(
            siteUrl=os.environ["SC_SITE"], body=body).execute()
        batch = res.get("rows", [])
        if not batch:
            return rows
        rows += batch
        start += 25000

1日ずつ取るのは、公式のガイドが「日次で1日分ずつ問い合わせる」方法を示しているためです。日ごとに蓄積しておけば、過去分の再取得が要りません。

直近の日は確定データが揃っていない

dataStateを省くと、確定データだけが返ります。前日分がまだ確定していない時期は、空や欠けが出ます。速報値が要るときはallを指定しますが、後で値が動くため、蓄積するデータは確定後のものに寄せます。CLAUDE.mdに「保存するのは確定データのみ」と書いておくと、Claudeが後から改修するときも方針がぶれません。

週次で順位とクリックの変化を出す

蓄積したCSVから、先週と今週の差分を出す集計も同じ要領で依頼できます。

data/gsc/の直近14日分から、クエリ別に今週と前週のクリック合計、
平均順位の差を出して。クリック差の上位10件と、順位が5位以上
悪化した10件をMarkdownの表にしてreport/weekly.mdへ書いて。

平均順位は、クリック数で重みを付けて集計するとぶれにくくなります。表示回数が数回のクエリは順位が大きく動くので、表示回数の下限を決めて足切りします。この条件も、依頼文に書いておきます。

定期実行の選び方 — /loop・Desktop・Routines・GitHub Actions

公式は、Claude Codeの定期実行を3種類に整理しています。

クラウド(Routines)Desktop/loop
実行場所クラウド(Routines)Anthropic管理のクラウドDesktop自分のマシン/loop自分のマシン
セッションを開いておくクラウド(Routines)不要Desktop不要/loop必要
ローカルファイルクラウド(Routines)使えない(新規クローン)Desktop使える/loop使える
最短間隔クラウド(Routines)1時間Desktop1分/loop1分

週次の観測に/loopを使うのは向きません。定期タスクはセッションに紐づき、作成から7日で自動的に期限切れになります。端末を閉じれば止まります。

サービスアカウントの鍵とCSVをローカルに置く構成なら、Desktopのスケジュールタスクが素直です。手順はClaude Code Desktopのスケジュールタスクにまとめています。

クラウドのRoutinesはローカルファイルを持てません。鍵を扱うなら、コネクタやシークレットの渡し方を先に決める必要があります。マシンを起動しておけない場合は、GitHub Actionsのscheduleトリガーという選択肢もあります。その場合は鍵をリポジトリのシークレットに置き、CSVはリポジトリかストレージへ書き戻します。

/loopが動かないときは、CLAUDE_CODE_DISABLE_CRONが設定されていないかを疑います。この変数は/loopとCron系ツールをまとめて止めるためです。/loopの基本は/loopコマンドの解説で扱っています。

特定のクエリやページだけ追う — フィルタと集計方式

全クエリを毎週見るより、追いたい語を絞る方が読みやすいレポートになります。dimensionFilterGroupsで、次元ごとの条件を指定できます。

演算子意味
equals(既定)意味完全一致。pageとqueryは大文字小文字を区別する
contains / notContains意味部分一致。大文字小文字を区別しない
includingRegex / excludingRegex意味RE2構文の正規表現

フィルタの対象には、グループ化していない次元も使えます。たとえばpageが/articles/を含む行だけをqueryで区切る、といった問い合わせが1回で済みます。groupTypeで指定できるのはandのみです。

集計方式のaggregationTypeにも注意が要ります。ページで区切る・絞るときはautoを選び、それ以外ではプロパティ単位(byProperty)かページ単位(byPage)かを選べます。ページで区切ると、プロパティ単位の集計は指定できません。同じ期間の数字が画面と食い違ったら、まず集計方式を疑います。

URL Inspectionは別のエンドポイント(searchconsole.googleapis.com/v1)で、上限も別です。サイトあたり2,000QPD・600QPMなので、順位の観測と分けて、インデックス状態を調べたいURLだけに使います。

方針をCLAUDE.mdとpermissionsに固定する

取得スクリプトは一度書いて終わりではなく、Claudeが何度も手を入れます。そのたびに前提がぶれないよう、守らせたい条件をプロジェクトのCLAUDE.mdに書いておきます。

## Search Console取得の規約
- 認証はサービスアカウント。鍵は環境変数SC_KEY_FILEで渡し、ファイルの中身は読まない
- スコープはwebmasters.readonlyのみ
- 1日ずつ取得し、data/gsc/に確定データだけを保存する
- 期間を広げた全件の取り直しはしない(負荷クォータのため)

鍵ファイルそのものは、Claudeに読ませない設定も併用できます。.claude/settings.jsonのdenyルールで、鍵を置くディレクトリへの読み取りを止めます。

{
  "permissions": {
    "deny": ["Read(./secrets/**)"]
  }
}

スクリプトは環境変数のパスだけを知っていればよく、Claudeが鍵の中身を見る必要はありません。

初回の動作確認

いきなり全期間を回さず、1日分で疎通を確かめます。

export SC_KEY_FILE=./secrets/sc-key.json
export SC_SITE="sc-domain:example.com"
python fetch_gsc.py --day 2026-09-20
head -5 data/gsc/2026-09-20.csv

権限の付き方を先に確かめたいときは、Sitesのlist(GET /sites)が便利です。ユーザーとして登録されたプロパティの一覧が返るので、追加した対象が見えていれば、招待は通っています。

403が返ったら、サービスアカウントがSearch Consoleのユーザーに入っているかを確認します。行が0件なら、日付とプロパティの種類を見直します。

落とし穴

  • プロパティの取り違え: https://のURLプレフィックスとsc-domain:では、見えるデータの範囲が違います。追加したプロパティの種類とスクリプトのsiteUrlを合わせます
  • 403が返る: サービスアカウントをSearch Console側に追加していないのが典型です。APIの有効化とは別作業です
  • 日付がずれる: 日付はPT時間で解釈されます。日本時間の感覚で「昨日」を指定すると、1日ずれることがあります
  • 画面と数字が合わない: ページで区切る問い合わせと、プロパティ単位の集計では、数え方が違います。正確な合計が要るときは、ページとクエリの次元を外して取ります
  • 鍵の扱い: JSON鍵をリポジトリにコミットしない。.gitignoreに加え、Claudeに読ませる範囲をpermissionsのdenyで絞る手もあります

まとめ

小中規模のサイトなら、API直叩きは準備が軽く、週次の変化を追うには十分です。1日ずつ取得して確定データだけを蓄積し、集計は蓄積したCSVから出します。鍵と方針はCLAUDE.mdとpermissionsで固定します。

クエリ数が多い大規模なサイトでは、1日50K行の上限が制約になります。定期実行は、鍵とCSVをローカルに置くならDesktop、マシンを起動しておけないならGitHub Actionsが候補です。

この記事を共有:XはてブLinkedIn