• トップ
  • ブログ一覧
  • Gemini API の Search Grounding で Google 検索を自動化する
  • Gemini API の Search Grounding で Google 検索を自動化する

    はじめに

    仕事してると「同じような検索を何十回もやってるな...」ってこと、ありませんか?
    エンジニアに限らずいろんな業種であると思います。

    • 営業の人が架電リストの会社を1件ずつ調べて電話番号メモる
    • 経理の人がインボイス対応で法人番号を国税庁サイトで1社ずつ検索する
    • マーケの人が競合企業の従業員数とか売上を調べまくる
    • 店舗の営業時間や住所をひたすらコピペする

    こういう「パターンは同じだけど、対象が違う検索」を何百回もやるの、正直しんどいですよね。

    最近はAIの発達がすごく、こういう作業はおおかた自動化できるようになっています。今回は、Gemini API とSearch Grounding を使って企業情報の保管をしていきましょう。これが簡単で結構すごくて、要は「Gemini に Google 検索させる」機能です。モデルが勝手に検索して、最新の情報を取ってきてくれます。

    この記事では、企業情報の取得を例に、Search Grounding の使い方とか、モデルごとの精度の違いとか、コストの話をまとめます。プロンプトを変えれば企業情報以外にも使えるので、参考になれば。

    実装には、Google Apps Script(GAS) を使いました。スプレッドシートと連携しやすいし、サーバー不要でサクッと動かせるので便利です。

    注意: Search Grounding を使っても精度は100%ではありません。あくまで「手作業を大幅に減らせる」ツールであって、重要なデータは最終的に人間が確認する必要があります。

    Gemini API のセットアップ

    事前準備

    Search Grounding を使うには、以下の準備が必要です。

    1. Google Cloud プロジェクトの作成 - Google Cloud Console で新規プロジェクトを作成
    2. 課金アカウントの設定 - クレジットカードを登録(無料枠内なら請求されない)
    3. Gemini API の有効化 - プロジェクトで「Generative Language API」を有効化

    この辺は「Gemini API 始め方」とかで検索すると詳しい記事がたくさん出てくるので、省略します!

    ちなみに、Vertex AI 経由でサービスアカウントや Google アカウント認証を使う方法もありますが、今回はサクッと試したかったので API キー方式にしました。本番環境で使う場合は、API キーの漏洩リスクを考えて Vertex AI + Google アカウント認証の方が良いかもしれません。

    API キーの取得

    1. Google AI Studio にアクセス
    2. 「Create API key」をクリック
    3. 発行された API キーをコピー

    GAS での設定

    GAS エディタで以下の手順で API キーを保存します。

    1. 「プロジェクトの設定」(歯車アイコン)を開く
    2. 「スクリプトプロパティ」セクションで「プロパティを追加」
    3. キー: GEMINI_API_KEY、値: 取得した API キー

    基本的な API 呼び出し

    1function callGeminiAPI(prompt) {
    2  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
    3  const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
    4
    5  const requestBody = {
    6    contents: [{
    7      role: 'user',
    8      parts: [{ text: prompt }]
    9    }],
    10    generationConfig: {
    11      temperature: 0.1,  // 正確性重視のため低めに設定
    12      maxOutputTokens: 4096
    13    }
    14  };
    15
    16  const options = {
    17    method: 'post',
    18    contentType: 'application/json',
    19    payload: JSON.stringify(requestBody),
    20    muteHttpExceptions: true
    21  };
    22
    23  const response = UrlFetchApp.fetch(endpoint, options);
    24  return JSON.parse(response.getContentText());
    25}

    モデル比較検証

    検証に使用したプロンプト

    以下のプロンプトで各モデルを検証しました(詳細は「プロンプト設計のポイント」を参照)。

    1あなたは日本の企業情報データベースの専門家です。
    2以下の情報から、企業の詳細情報を調査・推定してください。
    3
    4【入力情報】
    5- 会社名: 株式会社ライトコード
    6
    7【調査手順】
    81. 企業の正式なウェブサイトを検索
    92. 国税庁サイトで法人番号を検索
    103. 採用ページ等から従業員数を推定
    11
    12【出力形式】
    13JSON形式で以下を返してください:
    14{
    15  "companyName": "正式な会社名",
    16  "website": "https://example.co.jp",
    17  "houjinBangou": "1234567890123",
    18  "prefecture": "東京都",
    19  "employeeRange": "51-100名以下",
    20  "confidence": "high"
    21}
    22
    23【重要なルール】
    24- 不明な項目は null を使用
    25- URLは必ず http:// または https:// で開始

    検証したモデル

    モデル特徴
    gemini-2.0-flash高速・低コスト・バランス型
    gemini-2.5-flashFlash改良版・中コスト
    gemini-2.5-proPro・高精度・高コスト

    実際の検証結果:「株式会社ライトコード」での比較

    同一プロンプト + Search Grounding 有効で各モデルを検証しました。

    ※ Gemini 3.0系(gemini-3.0-flash / gemini-3.0-pro)は記事執筆時点でPreview版のため、今回の検証対象外としています。

    項目gemini-2.0-flashgemini-2.5-flashgemini-2.5-pro
    会社名株式会社ライトコード株式会社ライトコード株式会社ライトコード
    ウェブサイトhttps://rightcode.co.jp/https://rightcode.co.jp/https://rightcode.co.jp/
    法人番号7290801018296 ✅7290801018296 ✅7290801018296 ✅
    都道府県福岡県 ✅福岡県 ✅福岡県 ✅
    従業員数35-50名31-50名21-50名
    トークン数7443,2945,613

    モデル選定の結論

    モデル精度速度コスト総合評価
    gemini-2.0-flash採用
    gemini-2.5-flash-
    gemini-2.5-pro×-

    結論: 他の企業でもいくつか調べましたが、精度面ではモデル間の差はほとんど見られませんでした。主な違いはトークン消費量とコストです。コスト効率を考慮し、gemini-2.0-flash を採用しました。

    Search Grounding とは

    Gemini API に google_search ツールを有効化すると、モデルがリアルタイムで Google 検索を実行し、最新の情報を取得できます。

    有効化方法

    リクエストボディに tools パラメータを追加するだけです。

    1const requestBody = {
    2  contents: [{
    3    role: 'user',
    4    parts: [{ text: prompt }]
    5  }],
    6  tools: [{ google_search: {} }],  // ← これを追加
    7  generationConfig: {
    8    temperature: 0.1,
    9    maxOutputTokens: 4096
    10  }
    11};

    Search Grounding あり/なし の比較

    同一プロンプト、同一モデル(gemini-2.0-flash)で「株式会社ライトコード」を検証した結果

    項目Search なしSearch ありなしあり
    会社名株式会社ライトコード株式会社ライトコード
    ウェブサイトhttps://www.rightcode.co.jp/https://rightcode.co.jp/×
    法人番号2011001088 (10桁)7290801018296 (13桁)×
    都道府県東京都福岡県×
    従業員数11-50名35-50名
    信頼度highmedium--
    トークン数359744--

    Search なしは「自信満々で間違える」

    上記の結果で最も注目すべき点

    Search なしは confidence: "high" なのに、ほぼ全て間違っている!

    • 法人番号は 13桁 が正しいのに 10桁 を返している(明らかに捏造)
    • 本社は 福岡県 なのに 東京都 と回答

    これは LLM の「ハルシネーション(幻覚)」の典型例です。学習データにない情報を、もっともらしく捏造してしまいます。

    Search Grounding が実行した検索クエリ

    API レスポンスの groundingMetadata.webSearchQueries で、実際に実行された検索を確認できます。

    1[
    2  "株式会社ライトコード 公式サイト",
    3  "株式会社ライトコード 本社",
    4  "株式会社ライトコード 国税庁法人番号",
    5  "株式会社ライトコード 従業員数",
    6  "株式会社ライトコード 売上"
    7]

    プロンプトで「調査手順」を明示することで、Search Grounding が適切な検索を実行するよう誘導できます。

    なぜここまで差が出るのか

    Search なしの場合:

    • モデルの学習データ(2023年頃まで)に含まれる情報のみ
    • 法人番号のような最新データは取得できない
    • しかも「自信満々」で間違った情報を返す

    Search ありの場合:

    • リアルタイムで企業の公式サイトを検索
    • 国税庁サイトから法人番号を取得
    • 採用ページから従業員数を推定
    • 情報ソースが明確なので信頼性が高い

    コスト構造と管理

    料金体系

    ※ 2026年1月時点の情報です。最新の料金は公式ページをご確認ください。

    項目費用
    Gemini 2.0 Flash APIほぼ無料(微量のトークン課金)
    Search Grounding 無料枠(Free Tier)500回/日(Flash/Flash-Lite共有)
    Search Grounding 無料枠(Paid Tier)1,500回/日(Flash/Flash-Lite共有)
    Search Grounding 超過分$35/1,000リクエスト(約 ¥5.5/件

    注意点

    Search Groundingの無料枠はFree Tierで500回/日、Paid Tierで1,500回/日です。超過すると1件あたり約5.5円かかります。筆者は気づかずに8,000円ほど使ってしまいました...。

    無料枠のリセットタイミング

    • 太平洋時間(PT)午前0時

    大量処理する場合は、リセット時刻を跨いで2日に分けると無料枠を有効活用できます。

    プロンプト設計のポイント

    基本構造

    1あなたは日本の企業情報データベースの専門家です。
    2以下の情報から、企業の詳細情報を調査・推定してください。
    3
    4【入力情報】
    5- 会社名: ${companyName || "不明(要特定)"}
    6- Eメール: ${email}
    7- ウェブサイト: ${website}
    8
    9【調査手順】
    101. メールドメインから会社名を特定
    112. 企業の正式なウェブサイトを検索
    123. 国税庁サイトで法人番号を検索
    134. 採用ページ等から従業員数を推定
    14
    15【出力形式】
    16JSON形式で以下を返してください:
    17{
    18  "companyName": "正式な会社名",
    19  "website": "https://example.co.jp",
    20  "phone": "03-1234-5678",
    21  "houjinBangou": "1234567890123",
    22  "prefecture": "東京都",
    23  "employeeRange": "51-100名以下",
    24  "revenueRange": "1-5億円未満",
    25  "confidence": "high"
    26}
    27
    28【重要なルール】
    29- 不明な項目は null を使用("不明""なし" は禁止)
    30- URLは必ず http:// または https:// で開始
    31- 都道府県は47都道府県から選択

    精度を上げるコツ

    1. 調査手順を明示: Search Grounding が何を検索すべきか具体的に指示
    2. 出力形式を厳密に: JSON のキー名やバリデーションルールを明記
    3. 温度を低く設定: temperature: 0.1 で一貫性のある回答を得る
    4. フリーメール除外: Gmail 等からは会社名を推定しないよう指示

    大量データ処理の工夫

    GASのコードで工夫した箇所もいくつか挙げておきます。

    並列処理

    GAS の UrlFetchApp.fetchAll() で複数リクエストを並列実行します。

    1function getCompanyInfoBatch(dataList) {
    2  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
    3  const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
    4
    5  // リクエストを配列で準備
    6  const requests = dataList.map(data => ({
    7    url: endpoint,
    8    method: 'post',
    9    contentType: 'application/json',
    10    payload: JSON.stringify(buildRequestBody(data)),
    11    muteHttpExceptions: true
    12  }));
    13
    14  // 並列実行
    15  const responses = UrlFetchApp.fetchAll(requests);
    16
    17  return responses.map((response, index) => {
    18    try {
    19      return JSON.parse(response.getContentText());
    20    } catch (e) {
    21      return { error: e.message, index: index };
    22    }
    23  });
    24}

    GAS の実行時間制限対策

    GAS は 6分でタイムアウトするため、バッチ処理で対応します。

    1function runBatchProcess() {
    2  const BATCH_SIZE = 500;  // 1回の実行で処理する件数
    3  const PARALLEL_COUNT = 30;  // 同時実行数
    4  const START_TIME = new Date();
    5  const TIMEOUT_MS = 5 * 60 * 1000;  // 5分でストップ(余裕を持たせる)
    6
    7  // 処理対象を取得
    8  const rows = getUnprocessedRows();
    9
    10  for (let i = 0; i < rows.length; i += PARALLEL_COUNT) {
    11    // タイムアウトチェック
    12    if (new Date() - START_TIME > TIMEOUT_MS) {
    13      Logger.log(`タイムアウト: ${i}件まで処理完了`);
    14      return;
    15    }
    16
    17    const batch = rows.slice(i, i + PARALLEL_COUNT);
    18    const results = getCompanyInfoBatch(batch);
    19    writeResultsBatch(results);
    20
    21    Utilities.sleep(1000);  // API制限対策
    22  }
    23}

    バッチ書き込みで高速化

    1件ずつ書き込むと遅いため、範囲指定で一括書き込みします。

    1function writeResultsBatch(results) {
    2  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    3
    4  // 対象範囲の既存値を一括取得
    5  const range = sheet.getRange(startRow, startCol, results.length, colCount);
    6  const values = range.getValues();
    7
    8  // メモリ上で更新
    9  results.forEach((result, index) => {
    10    values[index][0] = result.companyName;
    11    values[index][1] = result.phone;
    12    // ... 他の項目
    13  });
    14
    15  // 一括書き込み
    16  range.setValues(values);
    17}

    まとめ

    今回の実装で得た知見

    1. Search Grounding は必須: Search なしでは法人番号が10桁(本来13桁)で返ってくるなど、致命的な誤りが発生
    2. Search なしの「高信頼度」は危険: confidence: "high" なのに間違っている = ハルシネーションの典型
    3. 無料枠を意識: Free Tierは500回/日、Paid Tierは1,500回/日。超過すると約 ¥5.5/件のコストが発生
    4. プロンプト設計が重要: 「調査手順」を明示することで Search Grounding が適切な検索を実行

    注意点:精度は100%ではない

    繰り返しになりますが、Search Grounding を使っても、取得できる情報の精度は100%ではありません。

    • 検索結果に情報がなければ取得できない(非公開企業、小規模事業者など)
    • 古い情報が検索上位に残っている場合、古いデータを返すことがある
    • 同名の別会社を誤って取得する可能性がある

    あくまで「手作業を大幅に減らすツール」として使い、重要なデータは最終的に人間がチェックすることをおすすめします。特にビジネスで使う場合は、取得したデータの正確性を確認する運用フローを組み込んでおくと安心です。

    参考リンク

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!カジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    あだっちー(エンジニア)
    あだっちー(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background