サイトのGA4測定ID・Search Consoleプロパティを人に画面を開かせず特定する
公開GTMコンテナからの測定ID抽出と、DWD impersonateによる全量取得。HTTP 200でも権限範囲の一覧を全量と誤認する事故の検知方法まで。
約3.8万トークンの節約 (API料金換算で約57円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「サイトのGA4測定ID・Search Consoleプロパティを人に画面を開かせず特定する」は、Google WorkspaceカテゴリのAI指示書(MDファイル)です。公開GTMコンテナからの測定ID抽出と、DWD impersonateによる全量取得。HTTP 200でも権限範囲の一覧を全量と誤認する事故の検知方法まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.8万トークン(API料金換算で約57円)・90%のトークンを節約できます。
- カテゴリ
- Google Workspace
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4.2万トークン
- この巻物使用時
- 約4,300トークン
- 節約量
- 約3.8万トークン (約57円)
- 更新日
- 2026-09-24
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/ga4-id-search-console/raw を読み込んで、この指示書どおりに実装して"
中身
サイトの GA4 測定ID・Search Console プロパティを、人に管理画面を開かせずに特定する
「このサイトの GA4 測定ID が分からない」「既存プロパティがあるのか新規作成が要るのか」を、 依頼者に画面キャプチャを取らせずに AI 側で確定させる手順。 最大の落とし穴は「API が 200 を返したのに答えが間違っている」ケースで、それをどう検知するかまで書く。
0. 結論(先に読む)
| 知りたいこと | 認証 | 取得方法 |
|---|---|---|
| サイトに今入っている測定ID | 不要 | 本番HTMLを grep |
| GTM コンテナの中の測定ID | 不要 | 公開コンテナ JSON を grep |
| そのプロパティが誰の所有か / 全プロパティ一覧 | 必要 | サービスアカウント + ドメイン全体の委任(DWD)で本人を impersonate |
| 直近のアクセス数 | 必要 | Analytics Data API runReport |
「存在しない」と断定してよいのは、本人を impersonate したトークンで一覧を取ったときだけ。
1. 認証ゼロで取れるところまで取る
1-1. 本番HTMLの計測タグ
curl -s https://<対象ドメイン>/ | grep -oE "G-[A-Z0-9]{6,12}|GTM-[A-Z0-9]{4,10}|UA-[0-9]{4,12}-[0-9]{1,4}"
1-2. GTM コンテナの中身(ここを飛ばす人が多い)
GTM コンテナIDさえ分かれば、コンテナ設定は認証なしで誰でも取得できる公開 JSON。
中に GA4 タグの測定IDが vtp_tagId":"G-..." の形で入っている。
curl -s "https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX" \
| grep -oE "G-[A-Z0-9]{6,12}|UA-[0-9]{4,12}-[0-9]{1,4}|AW-[0-9]{6,15}" | sort -u
重要: GTM 経由で読み込まれるタグは、保存済みHTMLの全文検索では絶対に拾えない。 「旧サイトのHTMLを全文検索したが UA しか無かった」という調査報告は、この理由でほぼ必ず取りこぼしている。 実例: HTML検索では UA しか出なかったサイトで、GTM コンテナを開いたら GA4 測定IDが入っていた。
1-3. アカウント番号の一致を手がかりにする
旧 UA の測定ID UA-<数字>-1 の <数字> は GA アカウントIDそのもの。
後述の一覧に accounts/<同じ数字> があれば、UA と GA4 が同居している=そこが探しているアカウント。
これは「既存プロパティを流用してよいか」の決定的な証拠になる。
2. 認証が要る部分:必ず impersonate で取る
2-1. 二つの認証方式の違い(ここが事故の温床)
| 方式 | 見える範囲 | 網羅性 |
|---|---|---|
SA 自身(sub なし) | その SA が明示的にユーザー追加されたものだけ | なし |
DWD impersonate(sub に本人のメール) | 本人が見られるもの全部 | あり |
一覧API(Search Console の sites、GA の accountSummaries、Drive の files.list など)は
トークンの持ち主の権限範囲しか返さない。これは仕様であって不具合ではない。
実害の例: 依頼者に「サービスアカウントを追加して」と頼み、1プロパティだけ追加された状態で一覧を取得。 API は 200 を返し 1件だけ返した。それを「アカウントにはこれしか無い」と報告したが、 実際には 12件あった。HTTP 200 は答えが正しい保証にならない。
2-2. セットアップ(一度きり)
- サービスアカウントを作り、鍵 JSON を取得
- Workspace 管理コンソールでドメイン全体の委任にそのクライアントIDを登録し、スコープを許可
- URL:
https://admin.google.com/ac/owl/domainwidedelegation - クリック順: メニュー → セキュリティ → アクセスとデータ管理 → API管理 → ドメイン全体の委任の管理
- スコープ:
https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/webmasters.readonly - 既存スコープを消さずに追記する(消すと既存の自動化が全部止まる)
- この登録に API は存在しない。人が画面でやる唯一の作業
- URL:
- GCP プロジェクトで対象 API を有効化
analyticsadmin.googleapis.com/searchconsole.googleapis.com/analyticsdata.googleapis.com
管理コンソールの
/ac/...系パスに/a/<ドメイン>/を挟むと 404 になる。 ドキュメント系ホストの URL 規約をそのまま当てはめないこと。
2-3. 実装(Node・依存ゼロ)
import { readFileSync } from 'node:fs';
import { createSign } from 'node:crypto';
const key = JSON.parse(readFileSync('<鍵JSONのパス>', 'utf8'));
const b64 = (o) => Buffer.from(typeof o === 'string' ? o : JSON.stringify(o)).toString('base64url');
async function getToken(scope, impersonate) {
const now = Math.floor(Date.now() / 1000);
const claim = { iss: key.client_email, scope, aud: 'https://oauth2.googleapis.com/token', exp: now + 3600, iat: now };
if (impersonate) claim.sub = impersonate; // ← これの有無が網羅性を決める
const unsigned = `${b64({ alg: 'RS256', typ: 'JWT' })}.${b64(claim)}`;
const sig = createSign('RSA-SHA256').update(unsigned).sign(key.private_key, 'base64url');
const res = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: `${unsigned}.${sig}`,
}),
});
const body = await res.text();
if (!res.ok) throw new Error(`HTTP ${res.status} ${body}`);
return JSON.parse(body).access_token;
}
叩くエンドポイント:
- GA4 一覧:
GET https://analyticsadmin.googleapis.com/v1beta/accountSummaries?pageSize=200 - ストリーム(測定ID):
GET https://analyticsadmin.googleapis.com/v1beta/properties/<id>/dataStreams→webStreamData.measurementIdとwebStreamData.defaultUri - Search Console 一覧:
GET https://www.googleapis.com/webmasters/v3/sites→siteUrlがsc-domain:始まりならドメイン型、https://…/ならURLプレフィックス型 - アクセス数:
POST https://analyticsdata.googleapis.com/v1beta/properties/<id>:runReport{ "dateRanges": [{"startDate": "30daysAgo", "endDate": "today"}], "metrics": [{"name": "activeUsers"}, {"name": "sessions"}, {"name": "screenPageViews"}] }
3. 誤答を防ぐ3つのガード(これが本体)
ガード1: impersonate を先に試す
SA 直接が先に成功すると、劣化した答えのまま完了してしまう。順序を固定する。
const MODES = [
{ sub: USER_EMAIL, label: 'impersonate(全量)', exhaustive: true },
{ sub: null, label: 'SA 自身(権限範囲のみ)', exhaustive: false },
];
// exhaustive:true が成功したらそこで打ち切る
ガード2: 出力に網羅性を明示する
### impersonate <本人>(本人が見える全量): HTTP 200
サイト 12 件 / 網羅性: あり
網羅性 なし の一覧に「存在しない」と書かせない。レポート本文にもこの1行を必ず持ち込む。
ガード3: 既知の値によるサニティチェック
「出てくるはずのもの」を先に宣言し、無ければ自分で警告を出す。
const EXPECTED = ['<対象ドメイン>', '<関連ドメイン>'];
const missing = EXPECTED.filter((d) => !found.some((s) => s.includes(d)));
if (missing.length) console.log(`⚠ サニティ警告: ${missing.join(', ')} が一覧に無い → 権限範囲不足の疑い`);
1件しか返らない一覧は原則疑う。 関連する既知ドメインが1つも出てこないのは、 「無い」よりも「見えていない」である確率のほうがはるかに高い。
4. エラー文の読み分け
| レスポンス | 意味 | 対処 |
|---|---|---|
unauthorized_client (401, トークン取得時) | DWD にそのスコープが未登録 | 管理コンソールでスコープ追記 |
SERVICE_DISABLED (403) | GCP プロジェクトで API が無効 | その API を有効化 |
Permission denied to enable service (403) | SA に API 有効化権限が無い | 権限のある人が1クリック、または SA にロール付与 |
| 200 だが件数が不自然に少ない | 権限範囲が狭い | impersonate に切り替える(← 最も危険。エラーが出ない) |
5. チェックリスト
- 本番HTMLを grep した
- GTM コンテナの公開 JSON を grep した(HTML検索だけで「無い」と言っていないか)
- 旧 UA のアカウント番号と GA アカウントIDを突き合わせた
- impersonate トークンで一覧を取った(SA 直接ではない)
- 出力に「網羅性: あり/なし」を明示した
- 既知ドメインのサニティチェックが通った
- ストリームURLが対象ドメインを向いているか確認した(旧サイト用プロパティの流用はデータが混ざる)
- 本番HTMLにタグが無いのにデータが流れていないか確認した(別経路からの送信=二重計測の予兆)
よくある質問
+「サイトのGA4測定ID・Search Consoleプロパティを人に画面を開かせず特定する」とは何ですか?
公開GTMコンテナからの測定ID抽出と、DWD impersonateによる全量取得。HTTP 200でも権限範囲の一覧を全量と誤認する事故の検知方法まで。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4.2万トークンかかりますが、この巻物を使えば約4,300トークンで済みます。差し引き約3.8万トークン(API料金換算で約57円)・90%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
GAS完全自動化テンプレ — Driveコマンドキュー方式
Google Apps Script の「毎回エディタで▶実行」を根絶。Drive 経由のコマンドキューで、初回1クリック以降は AI がすべての GAS 関数をリモート実行できるようになるテンプレート指示書。
人間の手入力台帳を壊さずに自動更新する — GAS Web App upsert 設計
各PC/各拠点の点検結果を、人間が手運用しているスプレッドシート台帳へ自動反映する。手入力列とコメントを絶対に壊さない突合設計、タブ/列の解決、並行POST対策、配布シークレットの落とし穴まで。
数式まみれの業務スプレッドシートを、Webアプリから壊さずに編集させる型
ArrayFormula と per-row 数式が混在する台帳を、セル単位 allowlist・dry-run 既定・適用前バックアップ・触っていないセルの数式不変検査で安全に書き換える設計手順。列ごとの数式復元と、テストが緑のまま壊れる典型例つき。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア