Search ConsoleのMCPにrowLimit 3を渡したら63行返ってきた。検索語別のクリックを足しても、ページの37%しか合わなかった

Search ConsoleのMCPにrowLimit 3を渡したら63行返ってきた。検索語別のクリックを足しても、ページの37%しか合わなかった

rowLimitに3を渡しました。返ってきた検索語別の行は63です。

iPhoneにipaファイルを入れる手順の記事(ipaインストールの記事)に、どんな検索語で人が来ているのか。AIエージェントからSearch ConsoleのMCPを呼んで調べている途中で、行数がおかしいことに気づきました。

行数は、指定した数字では決まりませんでした

同じ期間(2026年9月26日から10月22日)で、条件を変えながら6回呼びました。返り値はそれぞれJSONで保存してあります。

どの呼び出しで作ったJSONかは、次の表のとおりです。6回とも、共通の条件はsiteUrlがsc-domain:hibanas.net、期間が2026-09-26から2026-10-22、engineがgoogleです。

JSONの名前dimensionsフィルタ(page)rowLimit
q_limit40queryなし40
page_limit15pageなし15
page_contains_limit5pagecontains で how-to-install-ipa-itunes5
page_equals_limit5pageequals で ipa記事のURL5
q_contains_limit3querycontains で how-to-install-ipa-itunes3
q_contains_nodim_nolimit省略contains で how-to-install-ipa-itunes省略

engineにgoogleを指定したので、返り値は{"google": [行, 行, ...]}の形で返ります。check.pyの["google"]は、この行の配列を取り出しています。行数を数えたのが次の結果です。

python3 check.py
q_limit40              指定 40 / 返却  96
page_limit15           指定 15 / 返却 129
page_contains_limit5   指定  5 / 返却  28
q_contains_limit3      指定  3 / 返却  63
省略版 == rowLimit 3 版: True

指定した数と返ってきた数は、どれも合いません。40を頼んで96、3を頼んで63です。

GoogleのSearch Analytics APIの文書では、rowLimitは「返す行数の上限」です。dimensionsを指定しなければ、全部が1行にまとまるとも書いてあります。ところがこのMCPは、dimensionsとrowLimitを両方省いても、検索語別の63行を返しました。rowLimit 3の呼び出しと、中身は1行も違いませんでした。

原因がMCP側なのか、間に入るラッパーなのかは分かりません。私が見たのは返り値だけで、実装は読んでいないからです。確かなのは、このMCPでは「rowLimitで件数を絞る」前提を置けない、という一点でした。

検索語を足したら、クリックが合いませんでした

行数のずれに気づいたあと、別のことが気になりました。検索語別の行を全部足したら、ページの数字と合うのか。

ページの値は、URLを完全一致で指定した呼び出し(equals)から取りました。検索語別は、ページにhow-to-install-ipa-itunesを含む条件(contains)で、dimensionsをqueryにした呼び出しです。

見るものクリック表示回数
ipa記事のページ別の値741358
同じ記事の検索語別を足した値271191
検索語別が説明できる割合36.5%87.7%

クリックは、74件のうち27件しか検索語に結びつきません。残りの47件は、どの検索語から来たのか、この一覧からは分からないということです。

サイト全体でも確かめました。条件なしの検索語別96行(q_limit40)を足すとクリックは36件。ページ別129行(page_limit15)を足すと121件です。check.pyの4番目の集計で出していて、36を121で割ると29.8%です。検索語側は約3割しか見えていません。表示回数は、ページ別に見出し付きURLの行が混ざって二重になるので、ここでは比べていません。

Googleのヘルプには理由が書いてあります。クエリのタブでは、匿名化された(珍しい)検索語は表から外されます。ページやクエリで絞ったときの合計も、全体の合計とは合わないことがあるそうです。

このサイトの検索語は、63行のうち26行が表示1回だけでした。長い尾が続く形で、匿名化されやすい条件は揃っています。ただ、どの検索語がいくつ消えたのかは、APIから見えません。47件の中身は、今も確認できていません。

最初は、返ってきた一覧を全体だと思っていました

正直に書くと、最初に条件なしで96行が返ったとき、これで検索語の全体像が取れたと思いました。

ipa記事に絞った63行では、表示回数が最も多いのはipaファイル 開く iphoneで、178回です。クリックは1回で、クリック率は0.56%でした。この語に向けて書き足せばいい、と考えかけました。

その判断は、見えている3割から4割だけで決めることになります。残りの6割以上に、もっとクリックを取っている検索語があるかもしれません。確かめる方法がありません。

個人ビジネスの題材選びにAIを使うなら、ここは外せない前提だと思います。「何が検索されていますか」とAIに聞いて返ってくる答えは、見えている行だけの答えです。見えていない部分があることは、AIの文面には出てきません。

まだ納得していない点もあります。表示回数は87.7%まで合うのに、クリックは36.5%しか合いません。クリックを取っている検索語ほど、匿名化で外れやすいのか。それとも、別の理由があるのか。手元のデータでは切り分けられませんでした。

取り入れた確認

次の2つを、検索語別の一覧をAIに渡す前の手順に足しました。

  1. 返ってきた行数を、コードで数えます。頼んだ件数は信じません
  2. 検索語別の合計クリックを、ページ別(equals)の値と並べます。割合も出します

check.pyの中身は次のとおりです。長いので畳んでおきます。

check.pyの全文
import json

def rows(name):
    # engine に google を指定した返り値は {"google": [行, 行, ...]} の形
    return json.load(open(name + ".json"))["google"]

# 1. 指定した rowLimit と、実際に返ってきた行数
for name, asked in [("q_limit40", 40), ("page_limit15", 15),
                    ("page_contains_limit5", 5), ("q_contains_limit3", 3)]:
    print(f"{name:22} 指定 {asked:>2} / 返却 {len(rows(name)):>3}")

# 2. dimensions も rowLimit も省いた呼び出しは、rowLimit 3 の呼び出しと同じか
print("省略版 == rowLimit 3 版:", rows("q_contains_nodim_nolimit") == rows("q_contains_limit3"))

# 3. 検索語別の行を足した値と、ページの値
q = rows("q_contains_limit3")
page = rows("page_equals_limit5")[0]
qc = sum(r["clicks"] for r in q)
qi = sum(r["impressions"] for r in q)
print(f"ページ        クリック {page['clicks']:>3}  表示 {page['impressions']:>4}")
print(f"検索語別の和  クリック {qc:>3}  表示 {qi:>4}")
print(f"検索語別が説明できる割合  クリック {qc/page['clicks']:.1%}  表示 {qi/page['impressions']:.1%}")
print("表示1回だけの検索語:", sum(1 for r in q if r["impressions"] == 1), "/", len(q))
top = max(q, key=lambda r: r["impressions"])
print("表示が最多の検索語:", top["keys"][0], top["impressions"], "表示", top["clicks"], "クリック")

# 4. サイト全体(フィルタなし)の行を足したクリック
site_q = sum(r["clicks"] for r in rows("q_limit40"))
site_p = sum(r["clicks"] for r in rows("page_limit15"))
print(f"サイト全体  検索語別 {len(rows('q_limit40'))} 行の和 クリック {site_q}")
print(f"サイト全体  ページ別 {len(rows('page_limit15'))} 行の和 クリック {site_p}")
print(f"検索語別 / ページ別  {site_q/site_p:.1%}")

後半の出力は次のとおりです。

ページ        クリック  74  表示 1358
検索語別の和  クリック  27  表示 1191
検索語別が説明できる割合  クリック 36.5%  表示 87.7%
表示1回だけの検索語: 26 / 63
表示が最多の検索語: ipaファイル 開く iphone 178 表示 1 クリック
サイト全体  検索語別 96 行の和 クリック 36
サイト全体  ページ別 129 行の和 クリック 121
検索語別 / ページ別  29.8%

返り値は、最初は私が手でJSONに写しました。写し間違いが怖くなり、ツールの記録から返り値そのものを取り出して、手書きの版と1行ずつ一致することを確かめています。check.pyが読むのは、取り出したほうです。

出典

この記事をシェア