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に追加する
無人で動かすスクリプトには、ブラウザ操作が要らないサービスアカウントが向いています。手順は次の流れです。
- Google Cloudでプロジェクトを作り、Search Console APIを有効にする
- サービスアカウントを作成し、JSON鍵を発行する
- 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 += 250001日ずつ取るのは、公式のガイドが「日次で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が候補です。