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でAPIを有効にする
プロジェクトを作り、Search Console APIを有効にします。
- 2
サービスアカウントとJSON鍵を作る
鍵はスクリプトが読み込む認証情報になります。リポジトリには置きません。
- 3
Search Consoleのユーザーに追加する
プロパティの「Settings > Users and permissions」で、サービスアカウントのメールアドレスを入力して保存します。
見落としやすいのは3番目です。APIを有効にしただけでは、サービスアカウントはどのプロパティにも触れません。
ユーザー追加の画面が見えるのはプロパティのオーナーだけで、ヘルプは入力欄を「有効なGoogleアカウントのメールアドレス」と説明しています。メールグループは追加できません。サービスアカウント専用の手順はヘルプにないため、〜.iam.gserviceaccount.com形式のアドレスを同じ欄に入れます。
ユーザーの種類は、Owner・Full user・Restricted userの3段階です。Full userは「全データを閲覧でき、一部の操作もできる」、Restricted userは「大半のデータを閲覧できる」権限と説明されています。スクリプトが読むのはデータだけなので、Ownerを渡す必要はありません。Restricted userは閲覧できるデータが限られるため、取れない項目があればFull userにします。
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・date・hourなど。searchAppearanceは他の次元と並べて指定できない |
type | 内容web(既定)・image・video・news・discover・googleNews |
rowLimit | 内容1〜25,000。既定は1,000 |
startRow | 内容0始まりのオフセット。ページ送りに使う |
dataState | 内容省略時は確定データのみ。allで速報値を含む |
結果はクリック数の降順で返ります。dateで区切ったときだけ、日付の昇順です。データが無い日は結果から省かれるため、日付の抜けは「ゼロ」ではなく「行が無い」として現れます。
3つの上限は別々に効く
取れる量を縛る3つの上限
1回の応答
最大25,000行です。
startRowを25,000ずつ増やし、0行が返るまで繰り返します。1日分のデータ
1日・検索タイプごとに最大50K行(クリック順)までしか公開されません。
呼び出し
1サイトあたり1,200QPMです。別に、問い合わせの重さで決まる負荷クォータがあり、重い問い合わせでは超えることがあります。
50K行の上限があるため、クエリ×ページの細かい粒度では、長い裾野の一部が取れません。
負荷クォータには、10分単位の短期と1日単位の長期があります。どちらも同じ「quota exceeded」エラーで返ります。切り分けは次のとおりです。
- 15分待って通れば、短期の超過です。1日の中で問い合わせを分散させます
- 単発の問い合わせでも出るなら、長期の超過です。重い問い合わせを軽くします
重いのは、ページとクエリで区切る・絞る問い合わせです。両方を同時に使うものが最も重くなります。期間が長いほど負荷も増えます。
「毎週、過去6か月分を全部取り直す」設計は避けます。前回以降の日付だけを取り、手元に蓄積する形が向いています。
Claude Codeに取得スクリプトを書かせる
ここからがClaude Codeの出番です。仕様を渡して、蓄積まで含めて書かせます。依頼文の例を挙げます。
Search Console APIのsearchAnalytics.queryで、クエリ別のクリック・
表示・平均順位を1日ずつ取得するPythonスクリプトを書いて。
- 認証はサービスアカウントのJSON鍵(パスは環境変数SC_KEY_FILE)
- スコープはwebmasters.readonly、siteUrlは環境変数SC_SITE
- 対象は3日前から10日前までの日付(PT基準)。データが出ていない日は保存しない
- 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 += 250001日ずつ取るのは、公式のガイドが「日次で1日分ずつ問い合わせる」方法を示しているためです。日ごとに蓄積しておけば、過去分の再取得が要りません。
直近の日は確定データが揃っていない
公式のガイドによると、データが出そろうのは通常2〜3日後です。最新の取得可能日は、dateで区切った問い合わせを直近10日分で投げると分かります。依頼文で「3日前から10日前」とした理由はここにあります。
dataStateを省くと、確定データだけが返ります。allを指定すると速報値も含みます。dateで区切った問い合わせで、期間に未確定のデータが含まれると、レスポンスのmetadataにfirst_incomplete_date(PTの日付)が付きます。この日付以降の値は、あとから大きく動く可能性があります。時間帯別のhourで区切るならhourly_allを使います。
蓄積するのは確定後のデータに寄せます。CLAUDE.mdに「保存するのは確定データのみ」と書いておくと、Claudeが後から改修するときも方針がぶれません。
合計用と詳細用で問い合わせを分ける
クエリやページで区切った行を足し上げても、サイト全体のクリック数にはなりません。ページやクエリで区切ると、Googleが計算量を抑えるために一部のデータを落とすためです。そこで、1日分の取得を2種類の問い合わせに分けます。
1日あたり2本の問い合わせ
合計用
次元はcountryとdevice程度に抑え、pageとqueryは入れません。aggregationTypeはbyPageにします。ここで得た合計を、週次レポートの総クリックに使います。
詳細用
pageやqueryを入れて区切ります。一部のデータは落ちるので、行の合計は参考値に留め、順位の変化を見るクエリ単位の分析に使います。
合計用の本文は、たとえば次の形です。
{
"startDate": "2026-09-20", "endDate": "2026-09-20",
"dimensions": ["country", "device"],
"type": "web", "aggregationType": "byPage"
}typeをimageやvideoに変えれば、検索タイプごとの合計も取れます。ただし検索タイプごとに別の問い合わせが必要で、1日の公開行数の上限(50K行)も検索タイプ別に数えられます。
週次で順位とクリックの変化を出す
蓄積したCSVから、先週と今週の差分を出す集計も同じ要領で依頼できます。
data/gsc/の直近14日分から、クエリ別に今週と前週のクリック合計、
平均順位の差を出して。クリック差の上位10件と、順位が5位以上
悪化した10件をMarkdownの表にしてreport/weekly.mdへ書いて。行をまとめて平均順位を出すときは、各行のpositionを表示回数で重み付けする方法があります。表示回数が数回のクエリは順位が大きく動くので、下限を決めて足切りします。重み付けと下限は、依頼文に書いておきます。
定期実行の選び方 — /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日で自動的に期限切れになります。端末を閉じれば止まり、実行中に作業が長引いて予定時刻を過ぎた分も、アイドルになってから1回だけ走ります。セッションをバックグラウンドに回せば端末は不要になりますが、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)かを選べます。ページで区切ると、プロパティ単位の集計は指定できません。同じ期間の数字が画面と食い違ったら、まず集計方式を疑い、byPropertyとbyPageの両方で取って比べます。
searchAppearanceは、他の次元と並べて区切れません。まずsearchAppearanceだけで区切った問い合わせで、サイトに出ている種類の一覧を取ります。次に、その種類のどれか1つをdimensionFilterGroupsで絞り、deviceやpageなどを足して問い合わせます。種類が複数あれば、2段目は種類の数だけ必要です。
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日ずれることがあります
- 画面と数字が合わない: ページで区切る集計とプロパティ単位の集計では、数え方が違います。合計は前節の合計用の問い合わせで取り、
aggregationTypeをbyPropertyに替えた値と見比べます - 鍵の扱い: JSON鍵をリポジトリにコミットしない。
.gitignoreに加え、Claudeに読ませる範囲をpermissionsのdenyで絞る手もあります
まとめ
小中規模のサイトなら、API直叩きは準備が軽く、週次の変化を追うには十分です。1日ずつ取得して確定データだけを蓄積し、集計は蓄積したCSVから出します。鍵と方針はCLAUDE.mdとpermissionsで固定します。
クエリ数が多い大規模なサイトでは、1日50K行の上限が制約になります。定期実行は、鍵とCSVをローカルに置くならDesktop、マシンを起動しておけないならGitHub Actionsが候補です。