AIにX検索を外付けする|Grok APIでXの最新情報を集める

前回は、X の投稿を1件名指しして、正規の API 経由で取ってくる話を書きました。今回は向きが逆で、何があるか分からないものを探す側の話です。

この記事を読み終えると、X を情報源にした調べものを手元から回せるようになります。「この1週間、この話題について X で何が言われているか」を投げると、賛否が割れている論点と、その代表になっている投稿の URL が返ってくる。

新しいツールの不具合も、値上げへの反応も、使った人の詰まりどころも、記事にまとまる何日も前に X に出ます。なのに調べものを AI に任せると、そこだけ空白になるんですよね。それを埋めるのが Grok です。乗り換える話ではなくて、足りない X 検索だけを外から足す話をします。

今日話すのはこの3つです。

  • なぜ Grok だけ、何も繋がずに X を探せるのか
  • 叩き方と、返ってくるもの。絞り込みは効くけど、件数だけは決められない
  • 答えの中身を変えられるのは reasoning.effort だけ

払った額は出てきません。別の会社の規約で書けないので、X に直接お金を払う側の話は前回を見てください。あと、やるのは「探す」であって「全部集める」ではないです。Grok にはコーディング agent もありますが、扱うのは X 検索の部分だけです。

Grok なら、何も繋がずに X の中を探せます

手元の AI から問いを1つ投げると、Grok が検索の文を組み立てて X の投稿を引き、日本語の答えと引用された投稿の URL が返ってくる。往復は全部 xAI 側で起きていて、こちらが繋ぐものは無い

X を検索する道具が、最初から手元にある状態で始められます。繋ぐ作業も、X 側との別契約もいりません。追加の契約も実装もなしに X Search が最初から載っているのは、いまのところ Grok だけです。

なぜ、ほかの AI では返ってこないのか

前回、x.com/robots.txt を開いて、X が AI のクローラを名指しで締め出していることを確かめました。あのときは「だから自分でスクレイピングしてはいけない」という文脈で読んでいたんですが、同じ宣言が他の AI にも刺さっているんですよね。規約と宣言を守る側は通らない。だから返ってこない。

他の AI でも、前回紹介した公式 MCP サーバや X API の検索エンドポイントを自分で繋げば叩けます。ただしそれは、繋ぐ作業と X 側の従量課金という別の財布が要る話です。xAI は X を持っている側なので、この門の内側にいます。

外から来る AI(Claude や Codex)は x.com/robots.txt の Disallow: / の壁で門前払いになる。xAI(Grok)は X の内側にいて x_search を標準で持っている

紛らわしいのを一つ。GitHub Copilot でも Grok は選べますが(サポートされているモデルに Grok 4.5・4.6 が GA で載っています)、渡ってくるのはモデルだけで X Search は付いてきません。

用意するのは API キーと残高だけです

ここからは grok.com の画面に貼る話じゃなくて、手元から API を叩く話です。

用意するものは2つ。xAI のコンソールで発行する API キーと、前払いの残高です。従量課金なので、残高が無いと最初の1回も通りません。ここは前回の X API と同じですね。モデルは reasoning モデルの grok-4.6 を使います。

叩いてみると、返ってくるのは散文と URL です

エンドポイントは POST https://api.x.ai/v1/responses、認証は Authorization: Bearer $XAI_API_KEY。tools に {"type": "x_search"} を1つ入れるだけで X 検索が有効になります。

curl https://api.x.ai/v1/responses \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-4.6",
    "input": [{"role": "user", "content": "直近1週間の X で話題になっているものを教えて"}],
    "tools": [{"type": "x_search"}],
    "reasoning": {"effort": "low"}
  }'

最初につまずいたのが、返ってくるものの形です。検索結果の一覧じゃなくて、日本語の散文で書かれた回答と、引用された投稿の URL が返ってきます。骨組みだけ抜くとこうです(抜粋・省略あり)。

{
  "model": "grok-4.6",
  "reasoning": {"effort": "low"},
  "output": [
    {"type": "reasoning"},
    {"type": "custom_tool_call", "name": "x_keyword_search",
     "input": "{\"query\":\"(\\\"Claude Code\\\" OR Codex) (課金 OR コスト) min_faves:20 since:2026-09-04\",\"limit\":\"10\",\"mode\":\"Latest\"}"},
    {"type": "custom_tool_call", "name": "x_keyword_search", "input": "..."},
    {"type": "message",
     "content": [{
       "type": "output_text",
       "text": "直近1週間で盛り上がっていたのは……(日本語の散文が続く)",
       "annotations": [
         {"type": "url_citation", "url": "https://x.com/<handle>/status/<id>", "start_index": 334, "end_index": 392}
       ]
     }]}
  ],
  "usage": {
    "input_tokens": 46627, "output_tokens": 1875,
    "output_tokens_details": {"reasoning_tokens": 1306},
    "server_side_tool_usage_details": {"x_search_calls": 7}
  }
}

生の投稿データがそのまま落ちてくると思っていたので、ここで期待とズレました。「まず生データをもらってから自分で考える」というやり方が、この道具単体ではできないんですよね。探すところも読むところも、モデルにお任せする形になります。原文がどうしても要るなら、引用された URL を前回作った X API 側の取得に渡すことになります。

代わりに、モデルが実際に投げた検索クエリは custom_tool_call にそのまま残ります。何回検索したかは usage.server_side_tool_usage_details.x_search_calls に入っています。次の節の観測は、この2つを読んだものです。JSON のどこを読むかの注意は、記事の末尾に付録として置いておきます。

絞り込みは効きます。件数だけは決められません

ツール側には、公式に絞り込みの口があります(X Search のパラメータ、2026-09-14 時点)。

パラメータ何を絞るか
allowed_x_handles / excluded_x_handles投稿者のハンドル(各最大20件。両方を同時には指定できない)
from_date / to_date期間(ISO 8601)
enable_image_understanding / enable_video_understanding投稿内の画像・動画も読ませるか

僕は期間をツール側とプロンプトの両方に書きました。ツール側だけで効いているかは確かめていないんですが、プロンプト側は効いているのが見えます。「いいね20以上に絞って」「期間は 2026-09-04 以降」と日本語で書くと、モデルが組み立てた検索クエリに min_faves:20 と since:2026-09-04 が入っていました。lang:ja は頼んでもいないのに自分で使っていたくらいです。確認できるのはモデルが演算子を正しく組み立てることまでで、返ってきた投稿が本当に条件を満たしていたかは追っていません。

ところが、件数だけはどうやっても動きません。モデルが自分で決める limit は、どの条件を渡しても "10" で固定でした。「最大50件見て」と明示しても 10 のままです。代わりに検索の回数が増えます。返ってきた投稿の本文は入力トークンに積まれるので、回数が増えれば入力も増える。指示の出し方が消費に効いてくるのはこの経路です。

じゃあリクエスト側で止められないのかと REST リファレンスを見ると、口は2つあります。そして、2つとも効きません(2026-09-11 時点)。

パラメータ公式の説明実際どうなるか
search_parameters.max_search_results使用する検索結果の最大件数渡すと HTTP 410 が返って、リクエストごと死ぬ。Live search is deprecated. Please switch to the Agent Tools API というメッセージ付き
max_tool_callsこのレスポンスで許可するツール呼び出しの最大数2 を渡すと HTTP 200 で通って、エラーも無い。でも実際に走った検索は6回。同じ条件で付けずに投げたときは7回だった

前者は廃止された古い仕組みの残骸で、死ぬので間違えたことはその場で分かります。引っかかるのは後者で、上限を設定したつもりのまま、設定されないで走ります。死ぬほうがまだ親切だったな、と思いました。effort が low、max_turns が 6 の条件で1回試しただけなので、クライアント側のツールになら効くのかもしれません。

ただ、そもそも僕が欲しいのは件数じゃないんですよね。求めているのはトレンドを拾えることであって、正直、50件とかどうでもいい。だから触るのは、次のノブになります。

答えの中身は effort で変えられます

返ってくる答えの中身を動かせるのは、reasoning.effort だけでした。low か medium か high を渡します。既定は high なので、指定しないと一番重い設定で走ります(レスポンスの reasoning にこちらの設定が返ってくるので、省略して投げると high が入っているのが見えます)。

同じ問いを、low と high だけ変えて投げてみました。投げたのはこれです。

直近1週間の X で、Claude Code / GitHub Copilot / Gemini / Codex の「コスト事故・想定外の課金」
「エージェントの暴走」「権限設計・承認フロー」について盛り上がっている投稿を、いいね20以上に絞って
エンゲージメント順に調べて。話題を3〜5個のクラスタに分けて、各クラスタは次を各2行以内で書いて:
①論点(賛否が割れていればその対立も) ②代表投稿URL ③日本語圏か英語圏か。
実際に見た投稿だけに基づき、憶測は書かない。期間は 2026-09-04 以降に限る。
effort返ってきた内容検索回数入力トークン出力トークン
low話題の名前を並べるだけ。「対立は薄い」と書いて終わり746,6271,875
high論点と証拠。賛否が割れている対立を名指しで拾い、具体的な機構(サンドボックス設定でも許可なく外に書けた、といった中身)まで書いてくる。スレッドを辿る呼び出しも走った17166,4177,489

欲しかったのは後者です。話題の名前だけだと、「もう誰かが書いた話」で終わってしまうんですよね。

留保を一つだけ。これは同じ問いを1回ずつ投げただけで、対照も盲検もありません。X のタイムラインは刻々変わるので、同じ effort で2回投げても同じものは返ってきません。信じるかどうかは、自分の問いで一回試してみてほしいです。

残りの2つは、答えの中身を変えません。走りすぎを止める保険です。

  • max_turns はターン数の上限です。ただし公式が「呼び出し回数の上限ではない」とはっきり書いています。1ターンに複数の呼び出しが並列で載るからです
  • max_output_tokens は reasoning トークンを含めて止まるので、hard cap になるのは実質ここだけです。公式によると既定は 128,000
左がモデルが決めるもの(何件見るか、どう探すか)、右がこちらが決めるもの(どこまで考えさせるか=reasoning.effort、走りすぎを止める=max_turns と max_output_tokens)

最後にもう一つ。2026-09-21 の 12:00 PT から、X Search の課金単位が変わります。呼び出し1回あたりから、取れた投稿1件あたりへ、です。公式は「検索やスレッド取得で返った投稿は、親投稿も引用投稿も数える」と書いています。絞りたいのは件数なのに、件数を決めているのはモデルの側という噛み合わなさは、この日から効き方が変わってくるはずです。

で、これを何に使っているか

使い道は2つに寄っています。ひとつは、X にしか出ていない最新の動き。リリース直後の反応や不具合の報告みたいに、記事にまとまる前の段階のものです。もうひとつは、Web 検索では拾えない領域の調査。ブログや公式ドキュメントになったものは AI が普通に拾ってくるので、そこはもう困っていないんですよね。困るのは、まだ誰も記事にしていないのに X では話されている層です。

僕はこれを、自分が書こうとしている領域がもう誰かに埋められていないかの確認に使っています。

叩くたびに手で書くのは続かないので、自分用の skill と CLI にしました。配布はしていないので中身は省きますが、effort と max_turns をコマンドのフラグに出したのがポイントです。撃つ前にノブを決めないと投げられない形にしてあります。

冒頭の問いに戻ると、X だけ AI が拾ってこない、という話は Grok で埋まります。ただ、埋まり方は「検索結果がそのまま返る」じゃなくて、「探してきた誰かが要約を寄越す」なんですよね。そこは最後まで変わらないところです。

最初の一歩として渡すのは一つだけです。effort を明示して、1回投げてみてください。既定が high なので、省略するのは「一番重いのを選んだ」のと同じことになります。

付録:返ってきた JSON のどこを読むか

返ってきたものから何かを取り出すとき、どこを読むかの一覧です。

欲しいものどこを読むか落とし穴
回答本文output[] の type == "message" にある content[].textmessage は複数に分かれることがある。最後の一つだけ読むと本文を取り落とす
引用された投稿の URLoutput[].content[].annotations[] の type == "url_citation"ドキュメントは citations と呼んでいるが、レスポンスに citations というキーは無い
実行された検索output[] の type == "custom_tool_call"name は x_keyword_search / x_semantic_search / x_thread_fetch の3種。input にモデルが組んだ検索クエリがそのまま読める
検索した回数usage.server_side_tool_usage_details.x_search_calls指示の出し方で増えるのはここ
トークンusage.input_tokens / usage.output_tokens / usage.output_tokens_details.reasoning_tokens返ってきた投稿の本文は入力トークンに積まれる
引用の件数usage.num_sources_used信用しない。引用が7件あるのに 0 を返してきたことがある

もう一つ、送った設定が返りに出るかどうかは、効いたかどうかとは別の話です。

送ったもの返りに出るか効いたかの確かめ方
reasoning / toolsそのまま直下に返る返ってきた値を読めばいい
max_turns返ってこないx_search_calls の回数で見る
max_tool_callsnull で返る同上。null で返ってくること自体は、効かなかった証拠にはならない

ではまた!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。
エンジニア募集中!

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です