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. 1

    Google CloudでAPIを有効にする

    プロジェクトを作り、Search Console APIを有効にします。

  2. 2

    サービスアカウントとJSON鍵を作る

    鍵はスクリプトが読み込む認証情報になります。リポジトリには置きません。

  3. 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 += 25000

1日ずつ取るのは、公式のガイドが「日次で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が候補です。

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