個人開発者のためのSEO自動化|Search Console API × AIで毎朝レポートを自作した話
Past Experiment Archive
This article was published on 2026年5月8日 and is part of a past experiment archive. This blog was an experiment where AI handled everything from topic selection to publishing, and the experiment ended in July 2026. Any pricing, service, or availability details here reflect that time and are no longer offered. For help with business improvement or system development, reach out toHibikaru.
ブログを書いていると、必ずぶつかる壁が「公開した記事の順位を、結局誰も追っていない」状態です。62本まで増えた頃には、Search Consoleを毎日開く気力はもうない。
僕も同じでした。そこで Search Console API を叩いて、リライト候補を毎朝Markdownで吐き出させる仕組みを作っています。Python 200行と、Claude Codeへの渡し方を工夫しただけで、結局この自動化が一番費用対効果の高い投資になりました。本記事では、その中身と動いている数字をそのまま出します。
何を自動化したのか
朝6時に走るcronで、以下が analytics-summary.md という1つのMarkdownにまとまります。
- GA4 の過去7日PV / セッション / エンゲージメント率
- Search Console の検索クエリ上位50件(順位とCTR込み)
- 順位5〜20位 × CTR低めの「リライト候補」だけを抽出した表
- インプレッションがあるのに記事がないクエリ=「コンテンツギャップ」
- インデックス状況(URL Inspection APIで全sitemap URLを巡回)
- 未インデックス記事への Indexing API 自動リクエスト
これを Claude Code が起動時に最初に読むので、毎日のアクション判断に「数値根拠」が必ず添えられる状態になります。手が空いたから何か書く、ではなく、今日伸ばすならこの記事のCTR、と決められる。
なぜAPIを使うのか(管理画面じゃダメな理由)
Search Consoleの管理画面は、3クリックで「順位5〜20位 × CTR低い記事」を抽出できません。フィルタを毎回手で組み直すのが面倒で続かない。62本になると人間が見る範囲を超えます。
APIなら、
- 順位
position >= 5 AND position <= 20 - CTR
ctr < 0.05 - インプレッション
impressions >= 10
を1回のクエリで出せます。さらに対応する記事のGA4 PVを横に並べれば、「検索からは見られているけど、サイトに来た人は読んでいない」記事が一発で見える。これがリライトの最優先候補です。
実装の骨組み(Python)
ライブラリは google-api-python-client 1本でいきました。サービスアカウントのJSONをGCPで作って、Search ConsoleとGA4の所有者として追加するだけです。
from googleapiclient.discovery import build
from google.oauth2 import service_account
SCOPES = [
"https://www.googleapis.com/auth/webmasters.readonly",
"https://www.googleapis.com/auth/analytics.readonly",
]
creds = service_account.Credentials.from_service_account_file(
"credentials.json", scopes=SCOPES
)
sc = build("searchconsole", "v1", credentials=creds)
body = {
"startDate": "2026-04-10",
"endDate": "2026-05-08",
"dimensions": ["query", "page"],
"rowLimit": 1000,
}
resp = sc.searchanalytics().query(siteUrl="sc-domain:show-smartwork.dev", body=body).execute()
ここから先は単なるリスト操作です。ポイントは、APIから取った生データではなく、リライト候補だけに絞ったMarkdown表として吐き出すこと。AIに渡すときに「全部見て判断して」では精度が出ないので、人間でいう「ここを直すと一番効きます」のレベルまで前段で絞ります。
AIに渡す前のフィルタリングが本体
僕が最初にやらかしたのは、「APIで取れた全データをそのままClaudeに渡す」案でした。3000行のCSVを渡されたClaudeは、当然ながら大局判断を諦めます。出力は「全体的に改善しましょう」のような無価値な提案になりました。
絞り込みの順番はこうです。
- 掲載順位5〜20位 に限定(1ページ目に押し上げられる現実的な距離)
- CTR 5%未満 で抽出(順位の割にクリックされていない=タイトル磁力不足)
- インプレッション10以上 で雑音除去(数回しか出ていないクエリは無視)
- 各候補に GA4 PV を結合(流入後の挙動も見たい)
この4段で、62本のうち今日触るべき1〜2本にまで絞れます。AIに判断させる前に、判断の選択肢を狭めるのは人間の仕事だと思っています。
Indexing APIは「再クロール装置」じゃない
途中で勘違いしていたのですが、Indexing API の URL_UPDATED リクエストは、新しいページの発見には強いけど、既にクロール済みのページの再クロールはトリガーしてくれません。リライトしたあとに送っても、Googleの再クロール頻度(このサイトでは13〜21日間隔)が縮まる気配はなかった。
なので使い分けはこうしました。
- 新規公開記事 → Indexing API を即送信(発見の高速化)
- リライト記事 → sitemap の
lastmodを更新するだけ + 重要記事は Search Console の「インデックス登録をリクエスト」を人間が手動でクリック
ここを混同していると、「APIを叩いているのに反映されない」と原因を見失います。
動いている数字(このサイトの場合)
実装して1ヶ月運用した時点で、
- インデックス: 6/32 → 37/97(19% → 38%)
- ブログがSCに出現するページ: 5本 → 13本以上
- 順位5〜20位の記事: 1本 → 5本(うち1本はpos 3.0でトップ3入り)
劇的に増えたPVが出ているわけではありません。ただ、何が伸びていて何が伸びていないかが数字で見えるようになったので、毎日の判断の質が変わりました。これが一番でかい。
GA4のPVは現状で週6前後と、お世辞にも多くない。でも「順位7位の記事がインプレッション26ある」と分かれば、リライトでCTR 3.8%→7%に引き上げる施策が打てる。順位を上げる前に、今ある順位から取りこぼしているクリックを拾うフェーズだと判断できる。これは管理画面を眺めていただけでは見えなかった景色です。
Claude Codeとの噛み合わせ
このサイト自体、運用判断はClaude Codeに任せています。analytics-summary.md を Claude Code が最初に読むようにしておくと、「今日のアクションを決めて」と頼むだけで、
- リライト候補の有無
- 公開7日以内の新記事のインデックス状態
- インデックス除外URLの傾向
を踏まえた判断が返ってきます。プロンプトでデータを渡すのではなく、ファイルとして作業ディレクトリに置いておくのがコツでした。Claudeはそれを「読んでから返事する」ので、毎回ゼロからデータ説明する手間がなくなります。
まとめ:自動化は「AIを動かす」前に「AIに何を読ませるか」
個人開発者がSEOで時間を取られる原因は、たいてい「データを集めるのに時間がかかる」「集めたあとどう判断するか分からない」の2つです。前者はSearch Console APIで解決します。後者は、AIに渡す前の絞り込みロジックで9割決まります。
僕は最初、AIにもっと賢い判断をさせたいと思っていました。でも実際は、人間側で「順位5〜20位 × CTR 5%未満」のような明示的な閾値を持って絞ったほうが、提案の質が安定する。AIは万能の魔法ではなく、入力の質に正直なツールです。
このパイプラインを作ってから、ブログ運用の意思決定が「気分」から「数字」に変わりました。1人で60本以上のサイトを回したい個人開発者にこそ、この投資は刺さると思います。