Batch APIはいつ使うべき?通常APIとの違い・向いている処理を比較

Batch APIはいつ使うべき?通常APIとの違い・向いている処理を比較 AI開発

生成AI APIで大量の文章を分類したり、数万件の商品説明を生成したりすると、通常APIでは料金とRate Limitが問題になることがあります。

このような処理で検討したいのがBatch APIです。

Batch APIは複数のAIリクエストをまとめて送信し、プロバイダー側で非同期処理してもらう仕組みです。

2026年8月時点では、OpenAI、Claude、Geminiの主要なBatch APIはいずれも、通常の同期APIと比べて基本的なモデル利用料金を50%程度抑えられる設計になっています。ただし、リアルタイムで回答を受け取ることはできず、処理完了まで待つ必要があります。OpenAIは24時間以内の完了、Claudeは多くのBatchが1時間未満で完了しつつ24時間で期限切れ、Geminiは24時間を目標時間としています。

そのため、ユーザーが画面上で回答を待っているチャットには通常API、夜間処理や大量データの分類にはBatch APIという使い分けが基本です。

この記事では、Batch APIと通常APIの違い、向いている処理、向いていない処理、OpenAI・Claude・Geminiの違い、TypeScriptからBatch処理を実装する方法を解説します。

スポンサーリンク

Batch APIとは

通常のAI APIでは、アプリケーションからリクエストを送信すると、その接続上でモデルの処理完了を待ちます。

たとえばOpenAI Responses APIなら、次のように1件ずつ処理します。

OpenAI Responses API
import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const response =
  await openai.responses.create({
    model:
      process.env.OPENAI_MODEL!,
    input:
      "このレビューをpositiveまたはnegativeに分類してください。",
  });

console.log(
  response.output_text,
);

ユーザーが質問して、その場で回答を表示するチャットにはこの方式が向いています。

Batch APIでは処理方法が変わります。

大量のリクエストを一つのBatchとして登録し、APIからはまずBatch IDだけを受け取ります。

その後、プロバイダー側でバックグラウンド処理が進みます。

Batch処理の流れ
大量の処理対象を準備
↓
Batchを登録
↓
Batch IDを取得
↓
プロバイダー側で非同期処理
↓
完了状態を確認
↓
結果を取得
↓
DBへ保存

リクエストを送信したHTTP接続を何時間も維持するわけではありません。

そのため、大量処理を安価かつ高いスループットで実行できます。

OpenAIはBatch APIについて、同期APIより50%低い料金、同期APIとは別の高いRate Limit枠、24時間以内の完了という特徴を案内しています。

Batch APIと通常APIの最大の違いは速度ではなく即時性

Batch APIを理解するときは、「遅いAPI」と考えるより「結果を今すぐ必要としないAPI」と考えるほうが分かりやすくなります。

通常APIでは、ユーザーの操作とAI処理が直接つながっています。

通常API
ユーザー
↓
API
↓
AI
↓
回答
↓
ユーザーへ表示

Batch APIではユーザー操作から切り離します。

Batch API
DB
↓
Batch作成
↓
AIプロバイダー
↓
数分〜数時間後
↓
結果取得
↓
DB更新

実際にはBatch処理が数分で終了することもあります。

Claudeでは、多くのMessage Batchが1時間未満で終了すると案内されています。Geminiも24時間を目標としながら、多くの場合はそれより速く完了すると説明しています。

しかし、「普段は10分で終わるから10分以内に終わる前提」でサービスを設計してはいけません。

Batchは即時応答を保証するAPIではないため、処理完了時刻をユーザー体験の一部として保証する用途には通常APIを使います。

Batch APIなら料金を約半分にできる

Batch APIを使う大きな理由が料金です。

OpenAIのBatch APIは同期APIより50%低い料金です。ClaudeのMessage Batches APIも通常API価格の50%で課金されます。Gemini Batch APIも対応モデルのStandard API価格に対して50%の料金です。

たとえば通常APIで1日100ドル掛かっているオフライン処理をすべてBatchへ移行でき、同じトークン量・同じモデル・追加料金条件も同等であるなら、モデルの基本的な入出力料金は約50ドル相当まで下げられる計算になります。

ただし、単純にすべての請求額が半額になるとは限りません。

Prompt Caching、Web Search、Context Cacheの保存料金、その他のツール料金などは別の料金体系を持つ場合があります。

ClaudeではBatch割引とPrompt Cachingの割引を組み合わせることができます。GeminiでもBatchでContext Cachingを利用できますが、キャッシュヒット時は通常のContext Caching料金が適用されます。

したがって、実際のコスト削減率はリクエスト単位でトークン使用量をDBへ保存する方法で解説したように、Usageを実際に記録して確認するのが確実です。

通常APIのRate Limitを圧迫しにくい

大量データ処理では料金だけでなくRate Limitも問題になります。

たとえば数十万件の商品データを通常APIへPromise.all()で送ると、RPMやTPMの上限へ短時間で到達する可能性があります。

OpenAIのBatch APIには通常のモデルリクエストとは別のRate Limit枠があります。

OpenAIではBatch用に、Batch当たりのリクエスト数、モデルごとのQueued Prompt Tokens、Batch作成回数という独立した制限が設定されており、Batchで使用したトークンは通常API側のモデルRate Limitを消費しません。

そのため、

処理の分離
ユーザー向けチャット
→ 通常API

大量の夜間処理
→ Batch API

というように処理を分離できます。

日中の大量集計処理によって、ユーザー向けチャットまで429エラーになる問題を防ぎやすくなります。

Promise.allで大量送信するのとBatch APIは違う

Node.jsでは、複数の通常APIリクエストをPromise.all()で並列実行できます。

並列実行の例
const results =
  await Promise.all(
    records.map(
      async (record) => {
        return openai.responses.create({
          model:
            process.env
              .OPENAI_MODEL!,
          input:
            createPrompt(record),
        });
      },
    ),
  );

これはBatch APIではありません。

あくまで通常APIを大量に並列実行しているだけです。

通常APIのRate Limitが適用され、通常API料金で課金されます。

また、対象が10万件になれば、一度に大量のPromiseを生成する実装そのものにも問題があります。

Batch APIでは処理対象をプロバイダーへ渡し、キューイングと実行スケジュールをプロバイダー側へ任せます。

大量のオフライン処理では、アプリケーション側で複雑な並列数制御を行うよりBatch APIのほうが扱いやすい場合があります。

Batch APIが向いているのは独立した大量処理

Batch APIと相性がよいのは、一つひとつの処理が独立しているケースです。

たとえばECサイトの商品10万件について、商品説明を生成するケースを考えます。

商品Aの生成結果が商品Bの入力へ影響しないのであれば、各リクエストを独立して処理できます。

独立した処理
商品1 → 商品説明生成
商品2 → 商品説明生成
商品3 → 商品説明生成

このようなワークロードはBatch処理に非常に向いています。

OpenAIはBatch APIの代表的な用途として、大規模評価、データセット分類、コンテンツリポジトリのEmbedding、オフラインの動画生成を挙げています。Claudeも大規模評価、モデレーション、データ分析、大量コンテンツ生成を代表例としています。Geminiは大量データの前処理や評価など、即時回答を必要としない処理を推奨しています。

大量の商品説明生成に向いている

ECサイトの商品名、仕様、カテゴリなどから商品説明を生成する処理はBatch APIと相性がよい用途です。

新しく10万商品を登録するときに、管理者が一件ずつ生成完了を待つ必要はありません。

DBでは最初に次のような状態にします。

初期状態
商品A: ai_status = pending
商品B: ai_status = pending
商品C: ai_status = pending

夜間にpendingの商品をまとめてBatchへ登録します。

結果が戻ったら商品IDを使って更新します。

custom_idと商品IDの対応
product-1001 → 商品A
product-1002 → 商品B
product-1003 → 商品C

ユーザーが待っていない処理なので、数時間掛かってもサービス体験へほとんど影響しません。

料金を半分近くまで下げられるメリットのほうが大きくなります。

大量分類にも向いている

レビュー、問い合わせ、コメントなどの分類もBatch向きです。

たとえば100万件の問い合わせについて、

カテゴリ例
billing
technical
account
cancel
other

のようなカテゴリを付ける処理です。

1件の分類結果を数秒以内に取得する必要がなければ、リアルタイムAPIを使用するメリットは小さくなります。

特に過去データを一括してAI分析するときはBatch APIが有効です。

過去データの一括分析
過去1年の問い合わせ
↓
Batch分類
↓
カテゴリ別件数を集計
↓
分析画面へ反映

一度だけ大量に処理するデータ移行にも向いています。

Embeddingの作成にも向いている

RAG用に大量ドキュメントをベクトル化するときもBatch処理を検討できます。

たとえば10万件の記事を新しいEmbeddingモデルへ移行するとき、ユーザーが結果をリアルタイムに待つ必要はありません。

OpenAI Batch APIは/v1/embeddingsをサポートしており、1 Batchで最大50,000件のEmbedding入力という制限があります。GeminiにもBatch Embedding用APIが用意されています。

RAGのEmbeddingモデル変更では全チャンクを再Embeddingする必要があるため、対象データが大きいほどBatchのコスト削減効果も大きくなります。

ただし、本番データを一度に入れ替えず、

切り替え方式
旧Embeddingインデックス
+
新Embeddingインデックス

を並行して作成し、評価後に切り替えるほうが安全です。

AIモデルの評価にも向いている

モデルを変更するときは、数百〜数万件の評価プロンプトを同じモデルへ送ることがあります。

この処理もリアルタイムである必要がありません。

モデル評価の流れ
評価データ1,000件
↓
モデルAへBatch
↓
回答保存

評価データ1,000件
↓
モデルBへBatch
↓
回答保存

結果を比較

OpenAIとClaudeはいずれも大規模EvaluationをBatchの代表的な用途として案内しています。

モデル更新のたびに大量の評価を行う環境では、通常APIからBatchへ切り替えるだけでも継続的な評価費用を大きく抑えられます。

夜間バッチとの相性がよい

Webサービスでは、リアルタイム処理とBatch処理を完全に分ける構成が扱いやすくなります。

昼間は通常APIを使います。

昇間の処理
ユーザーの質問
↓
通常API
↓
即時回答

深夜にはDBから未処理データを取得します。

深夜の処理
0:00
↓
未処理データ取得
↓
Batch登録
↓
バックグラウンド処理
↓
翌朝までに結果反映

「翌日までに終わればよい」処理はBatch APIの有力候補です。

チャットにはBatch APIを使わない

一般的なAIチャットでは、ユーザーが送信ボタンを押した直後から回答を待っています。

Batch APIでは処理開始から結果取得までの時間をリアルタイム用途向けに保証できません。

チャットでのBatchは不適切
ユーザー「このエラーの直し方を教えて」
↓ Batchへ登録
数十分後
回答完成

これでは通常のチャットとして成立しません。

ユーザーが画面上で回答を待っている場合は通常APIを使用します。

特にストリーミングで一文字ずつ回答を表示するUIはBatchではなく通常API向きです。

ClaudeのMessage Batchesでもstream: trueはサポートされず、結果はBatch完了後に取得する方式です。

AIエージェントにも通常APIのほうが向いている

AIエージェントでは、モデルがツールを選択し、アプリケーション側がツールを実行し、その結果をもう一度モデルへ返す処理が発生します。

ツール実行ループ
LLM
↓
search_customer
↓
アプリが実行
↓
結果をLLMへ返す
↓
create_order
↓
アプリが実行
↓
最終回答

クライアント側でツールを実行する構成では、1回のモデル応答ごとにアプリケーション側の処理が必要です。

Batchへ投入して数時間後にFunction Callを受け取り、その結果をさらに次のBatchへ入れる構成にすると、非常に長いワークフローになります。

対話型のエージェントでは通常APIのほうが実装しやすくなります。

ただし、すべてのツール処理がプロバイダー側で完結するケースは別です。

ClaudeのMessage Batchesでは、Web Search、Web Fetch、Code Execution、MCP ConnectorなどのServer ToolsもBatch内で実行できます。サーバー側のAgentic LoopがBatch Worker内で実行されます。

「Function CallingがあるからBatchは使えない」と一律に判断するのではなく、誰がツールを実行するかを確認する必要があります。

1件だけでもBatch APIを使うべきか

Batch APIに「最低100件から使うべき」という共通ルールはありません。

重要なのは件数より即時性です。

たとえば1件の処理でも、非常に長い入力と出力があり、結果を翌日まで待てるのであればBatchによる料金削減には意味があります。

一方、1件あたり0.001ドルの短い処理を10件だけ実行する場合、JSONL作成、Batch登録、状態管理、結果取得まで実装するコストのほうが大きくなります。

実務では、

判断の観点
結果をすぐ必要とするか
処理量が継続的に多いか
通常APIの料金が問題になっているか
Rate Limitが問題になっているか
Batch管理を追加する価値があるか

という観点で判断します。

件数だけで通常APIとBatch APIを切り替えないことが重要です。

OpenAI Batch APIでは最大50,000リクエスト

OpenAIでは、一つのBatchへ最大50,000リクエストを登録できます。

入力JSONLファイルは最大200MBです。

また、Batch作成自体は1時間あたり最大2,000件で、モデルごとにQueued Prompt Tokensの上限もあります。BatchのRate Limitは通常APIとは別枠です。

50,000件を超えるデータは複数Batchへ分割します。

Batch分割
全データ 120,000件

Batch A: 0~49,999
Batch B: 50,000~99,999
Batch C: 100,000~119,999

1つの巨大Batchだけに依存するより、適度なサイズへ分割したほうが失敗時の再実行もしやすくなります。

Claudeは最大100,000リクエスト

Claude Message Batchesでは、一つのBatchにつき100,000 Messageリクエストまたは256MBのどちらか先に到達した値が上限です。

処理が24時間以内に完了しなかったリクエストは期限切れとなります。結果はBatch作成後29日間取得できます。

Claudeでは各リクエストにcustom_idを設定します。

Claude Message Batches
const batch =
  await anthropic.messages.batches.create({
    requests: [
      {
        custom_id:
          "product_1001",
        params: {
          model:
            process.env
              .ANTHROPIC_MODEL!,
          max_tokens: 500,
          messages: [
            {
              role: "user",
              content:
                "商品説明を作成してください。",
            },
          ],
        },
      },
      {
        custom_id:
          "product_1002",
        params: {
          model:
            process.env
              .ANTHROPIC_MODEL!,
          max_tokens: 500,
          messages: [
            {
              role: "user",
              content:
                "商品説明を作成してください。",
            },
          ],
        },
      },
    ],
  });

ClaudeのBatch結果は入力した順番と同じ順番で返る保証がありません。

そのため、配列のインデックスではなくcustom_idで元データと対応させます。

GeminiはInlineとJSONLの2方式がある

Gemini Batch APIには、リクエストをBatch作成リクエストへ直接含めるInline方式と、JSONLファイルをアップロードする方式があります。

Inline方式は合計リクエストサイズが20MB未満の小規模Batch向けです。

大量データではJSONLファイル方式が推奨され、入力ファイルは最大2GBです。

JavaScriptでは小規模Batchを次のように作成できます。

Gemini Batch API
import {
  GoogleGenAI,
} from "@google/genai";

const ai = new GoogleGenAI({
  apiKey:
    process.env.GEMINI_API_KEY,
});

const batch =
  await ai.batches.create({
    model:
      process.env.GEMINI_MODEL!,
    src: [
      {
        contents: [
          {
            role: "user",
            parts: [
              {
                text:
                  "レビューAを分類してください。",
              },
            ],
          },
        ],
      },
      {
        contents: [
          {
            role: "user",
            parts: [
              {
                text:
                  "レビューBを分類してください。",
              },
            ],
          },
        ],
      },
    ],
    config: {
      displayName:
        "review-classification",
    },
  });

console.log(batch.name);

2026年8月時点ではGemini Batch APIはgenerateContent系で利用する機能であり、Interactions API用のBatchではありません。

通常APIでInteractions APIを使っているプロジェクトでは、この違いに注意が必要です。

OpenAI Batch APIをTypeScriptで実装する

OpenAIではJSONLファイルを作成し、Files APIへアップロードしてからBatchを作成します。

Responses APIもBatch対象です。OpenAIは現在、Responses、Chat Completions、Embeddings、Completions、Moderations、Images、Videosなど複数のEndpointをBatchでサポートしています。

最初に処理対象を用意します。

商品データ
type Product = {
  id: number;
  name: string;
  description: string;
};

const products: Product[] = [
  {
    id: 1001,
    name: "Product A",
    description:
      "軽量なワイヤレスキーボード",
  },
  {
    id: 1002,
    name: "Product B",
    description:
      "USB接続のコンパクトマウス",
  },
];

JSONLを作成します。

src/build-batch-input.ts
import {
  writeFile,
} from "node:fs/promises";

const model =
  process.env.OPENAI_MODEL;

if (!model) {
  throw new Error(
    "OPENAI_MODELが設定されていません。",
  );
}

const lines =
  products.map((product) => {
    return JSON.stringify({
      custom_id:
        `product_${product.id}`,
      method: "POST",
      url: "/v1/responses",
      body: {
        model,
        instructions:
          "商品情報から100文字程度の商品説明を作成してください。",
        input: `
商品名:
${product.name}

商品情報:
${product.description}
        `.trim(),
      },
    });
  });

await writeFile(
  "batch-input.jsonl",
  `${lines.join("\n")}\n`,
  "utf8",
);

OpenAIでは各リクエストへ一意なcustom_idを付けます。

結果の並び順は入力順と一致する保証がないため、product_1001のようにDB上のデータと結び付けられるIDを使用します。

JSONLをOpenAIへアップロードする

作成したファイルをFiles APIへ送信します。

src/upload-batch-input.ts
import fs from "node:fs";
import OpenAI from "openai";

const openai =
  new OpenAI({
    apiKey:
      process.env.OPENAI_API_KEY,
  });

const inputFile =
  await openai.files.create({
    file:
      fs.createReadStream(
        "batch-input.jsonl",
      ),
    purpose: "batch",
  });

console.log(inputFile.id);

Batch用ファイルはpurpose: "batch"としてアップロードします。

アップロードしたファイルIDを使ってBatchを作成します。

src/create-batch.ts
const batch =
  await openai.batches.create({
    input_file_id:
      inputFile.id,
    endpoint:
      "/v1/responses",
    completion_window:
      "24h",
    metadata: {
      operation:
        "product_description",
    },
  });

console.log({
  batchId:
    batch.id,
  status:
    batch.status,
});

OpenAIでは現在、completion_window24hを指定します。

APIが返したbatch.idは必ずDBへ保存します。

Batch IDをDBへ保存する

Batchを作成した直後にプロセスが停止しても、後から処理を再開できるようにします。

schema.sql
CREATE TABLE ai_batches (
  id BIGSERIAL PRIMARY KEY,

  provider VARCHAR(32)
    NOT NULL,

  provider_batch_id
    VARCHAR(255)
    NOT NULL,

  operation VARCHAR(100)
    NOT NULL,

  status VARCHAR(32)
    NOT NULL,

  submitted_at
    TIMESTAMPTZ NOT NULL,

  completed_at
    TIMESTAMPTZ,

  created_at
    TIMESTAMPTZ
    NOT NULL DEFAULT NOW(),

  UNIQUE (
    provider,
    provider_batch_id
  )
);

さらに、Batch内部の各リクエストも別テーブルへ保存します。

schema.sql
CREATE TABLE ai_batch_items (
  id BIGSERIAL PRIMARY KEY,

  batch_id BIGINT NOT NULL
    REFERENCES ai_batches(id),

  custom_id VARCHAR(255)
    NOT NULL,

  source_id VARCHAR(255)
    NOT NULL,

  status VARCHAR(32)
    NOT NULL DEFAULT 'pending',

  result JSONB,

  error JSONB,

  UNIQUE (
    batch_id,
    custom_id
  )
);

この構成にすると、

追跡できる情報
どのBatchに入れたか
元の商品IDは何か
成功したか
失敗したか
再処理が必要か

を追跡できます。

Batchの状態を確認する

OpenAIではbatches.retrieve()で現在の状態を取得できます。

src/poll-batch.ts
const current =
  await openai.batches.retrieve(
    batch.id,
  );

console.log({
  status:
    current.status,
  completed:
    current.request_counts
      ?.completed,
  failed:
    current.request_counts
      ?.failed,
  total:
    current.request_counts
      ?.total,
});

OpenAI Batchはvalidatingから始まり、in_progressfinalizingcompletedなどへ遷移します。

24時間以内に処理できなかった場合はexpiredになります。キャンセル中はcancelling、完了するとcancelledになります。

定期ジョブで状態を確認する場合は、数秒ごとに大量のPollingを行う必要はありません。

数十秒から数分程度の間隔で確認し、リアルタイム性を求めない設計にします。

完了した結果を取得する

Batchがcompletedになったらoutput_file_idを取得します。

src/fetch-batch-output.ts
const completed =
  await openai.batches.retrieve(
    batch.id,
  );

if (
  completed.status !==
    "completed" ||
  !completed.output_file_id
) {
  throw new Error(
    "Batchはまだ完了していません。",
  );
}

const response =
  await openai.files.content(
    completed.output_file_id,
  );

const outputText =
  await response.text();

出力もJSONLです。

出力のパース
const results =
  outputText
    .split("\n")
    .filter(Boolean)
    .map((line) =>
      JSON.parse(line),
    );

結果を元データへ戻すときは順番ではなくcustom_idを使います。

結果の照合
for (
  const result of results
) {
  const customId =
    result.custom_id;

  if (
    typeof customId !==
    "string"
  ) {
    continue;
  }

  console.log({
    customId,
    statusCode:
      result.response
        ?.status_code,
    error:
      result.error,
  });
}

OpenAI公式ドキュメントでも、出力行の順番は入力と一致しない可能性があるため、custom_idを利用するよう明記されています。

配列の順番でDBを更新してはいけない

Batch実装で特に危険なのが、入力順と出力順が同じだと仮定することです。

入力が次の順番だったとします。

入力順
product_1001
product_1002
product_1003

結果は次の順番になる可能性があります。

出力順
product_1003
product_1001
product_1002

この状態で、

危険な例
products[index]

を使って更新すると、別の商品へAI生成結果を保存してしまいます。

OpenAIとClaudeはいずれもBatch結果の順番が入力順と一致する保証がないと説明しています。

必ず自分で設定した識別子を使います。

安全な例
const productId =
  customId.replace(
    "product_",
    "",
  );

より安全にするなら、変換規則ではなくDBのai_batch_itemsテーブルで対応関係を管理します。

Batch全体が成功しても各リクエストは失敗する

Batchの状態が完了していても、すべてのリクエストが成功したとは限りません。

入力内容の不備、モデル側のエラー、期限切れなどによって一部だけ失敗する可能性があります。

Claudeでは各結果がsucceedederroredcanceledexpiredに分かれます。エラー、キャンセル、期限切れでモデルへ送信されなかったリクエストには料金が発生しません。

OpenAIでも成功結果はOutput File、失敗したリクエストはError Fileから確認できます。24時間でBatchが期限切れになった場合も、完了済みのリクエストについては結果を取得でき、実際に処理されたトークンには料金が発生します。

そのため、

設計上の注意
Batch completed
≠
全件成功

ではありません。

Batch単位とItem単位のステータスを別々に管理します。

失敗したItemだけを再Batchする

10万件のうち100件だけ失敗した場合、10万件すべてをもう一度送る必要はありません。

失敗Itemの抽出
SELECT
  *
FROM ai_batch_items
WHERE
  batch_id = $1
  AND status IN (
    'failed',
    'expired'
  );

失敗したItemだけから新しいJSONLを作ります。

再Batchの考え方
Batch 1
100,000件
↓
99,900件成功
100件失敗

Batch 2
失敗した100件だけ再送

この方式なら、同じ成功データを二重課金する問題を避けられます。

ただし、入力形式が不正な400系エラーをそのまま再送しても再び失敗します。

エラー内容によって再試行するか、人間の確認へ回すかを分けます。

Batch作成処理にも冪等性を持たせる

Batch作成APIのレスポンスを受け取れなかったからといって、そのまま同じデータでもう一度Batchを作ると、二つのBatchが作成される可能性があります。

GeminiはBatch作成が冪等ではなく、同じ作成リクエストを2回送れば別々のBatch Jobが作成されると明記しています。

アプリケーション側でBatch Job IDを発行し、送信前にDBへ記録します。

内部IDの例
internal_batch_id
=
batch_20260809_001
schema.sql
INSERT INTO ai_batches (
  provider,
  provider_batch_id,
  operation,
  status,
  submitted_at
)
VALUES (
  'openai',
  'pending',
  'product_description',
  'creating',
  NOW()
);

同じ対象範囲についてcreatingまたはsubmittedのBatchが存在する場合は、新しいBatchを作成しないようにします。

大量処理では、モデルの再試行よりBatchそのものの重複送信のほうが大きな料金事故につながることがあります。

Prompt CachingとBatch APIは競合するとは限らない

大量のリクエストで同じ長いシステムプロンプトやドキュメントを使用する場合は、Prompt Cachingも検討できます。

ClaudeではMessage BatchesとPrompt Cachingを組み合わせられ、Batch割引とPrompt Cachingの割引を重ねることができます。

ただしBatchは非同期・並列処理されるためキャッシュヒットはBest Effortで、Claudeは共有コンテキストを持つBatchでは1時間キャッシュの利用も検討するよう案内しています。

GeminiもBatch Requestから既存のCached Contentを参照できます。キャッシュヒットした場合は通常のContext Caching料金が適用されます。

「BatchにしたからPrompt Cachingは不要」と一律には判断できません。

大量の共通プレフィックスを何度も処理する場合は、両方を評価します。

Structured OutputsもBatchで使える場合がある

大量の分類やデータ抽出では、自由な文章ではなくJSONとして結果を受け取りたいケースがあります。

Gemini Batch APIではStructured Outputsをリクエスト設定へ含められます。Claudeも通常のMessages APIで利用できる機能の多くをMessage Batchesへ含められます。

OpenAI Batchでも、対象EndpointのBodyには基本的にそのEndpointで使用するパラメータを設定します。

ただし、Batch結果だからJSONが必ず業務要件を満たしているとは限りません。

Structured Outputs・Zodで安全に検証する方法で解説したように、結果取得後はZodなどで検証します。

検証例
const parsed =
  ProductDescriptionSchema
    .safeParse(
      responseBody,
    );

if (!parsed.success) {
  // 再処理対象へ
}

Batch処理では人間がリアルタイムで結果を確認しないことが多いため、むしろ出力検証を自動化する重要性が高くなります。

通常APIとBatch APIを自動で振り分ける

同じ機能でも、状況によって通常APIとBatch APIを使い分けられます。

たとえば記事要約機能で、

使い分けの例
管理画面から「今すぐ要約」
→ 通常API

過去記事10万件を一括要約
→ Batch API

と分けます。

アプリケーション内部では同じタスク形式にできます。

src/ai-task.ts
type AiTask = {
  id: string;
  operation:
    "summarize";
  input: string;
  priority:
    "realtime" |
    "background";
};

ルーティングします。

src/route-ai-task.ts
async function enqueueAiTask(
  task: AiTask,
): Promise<void> {
  if (
    task.priority ===
    "realtime"
  ) {
    await runRealtime(task);
    return;
  }

  await savePendingBatchTask(
    task,
  );
}

バックグラウンドタスクはDBへ貯めます。

一定件数または一定時間でまとめてBatchへ送ります。

タスクの状態遷移
pending
↓
batched
↓
processing
↓
completed

この設計なら、機能側でOpenAI Batch APIの細かな仕様を意識する必要がありません。

何件たまったらBatchを作るか

すべての処理で「1,000件になったら送る」という固定ルールにする必要はありません。

処理件数と最大待機時間の両方を設定すると扱いやすくなります。

src/batch-trigger-config.ts
const MAX_BATCH_SIZE =
  10_000;

const MAX_WAIT_MINUTES =
  30;

10,000件に達したらすぐ送信します。

10,000件に達していなくても、最も古いタスクが30分待っていれば送信します。

送信条件
条件A: 10,000件に到達 → Batch作成
条件B: 30分経過 → 現在たまっている分でBatch作成

低トラフィック時にいつまでもタスクが処理されない問題を防げます。

Batch APIを使う判断基準

ユーザーがその場で結果を待っているなら、通常APIを選びます。

結果が数十分から数時間後でも問題なく、同じ種類の処理を大量に実行するならBatch APIを検討します。

さらに、通常APIの料金が大きくなっている場合、RPMやTPMが大量処理で圧迫されている場合、夜間処理や過去データ分析などユーザー操作から分離できる場合は、Batchへ移行する効果が大きくなります。

逆に、チャット、インタラクティブなコード補完、リアルタイム検索、クライアント側Function Callingを繰り返すAIエージェントなどは通常APIが適しています。

つまり判断基準は、

判断基準ではない
大量かどうか

だけではありません。

最も重要なのは、

最も重要な判断基準
結果を今すぐ必要とするか

です。

OpenAI・Claude・Geminiの違いも確認する

OpenAIではJSONLファイルをアップロードしてBatchを作る方式が基本で、1 Batchは最大50,000リクエスト・200MBです。現在のCompletion Windowは24時間で、通常APIとは別のBatch Rate Limit枠があります。

Claude Message Batchesでは最大100,000リクエストまたは256MBで、多くのBatchが1時間未満で終了します。24時間で期限切れとなり、結果は作成から29日間取得できます。通常のMessages API機能の多くを利用でき、Server ToolsやExtended Thinkingにも対応しています。

GeminiではInline方式とJSONL方式があり、Inlineは20MB未満の小規模処理、JSONLは最大2GBの大量処理向けです。BatchはgenerateContent APIで提供され、EmbeddingのBatch処理にも対応しています。

3社とも「Batchだから使い方が完全に同じ」ではありません。

プロバイダーを切り替える場合は、OpenAI・Claude・Gemini APIを自動で切り替えるフォールバック設計とは別に、Batch Adapterを用意する方法が扱いやすくなります。

Batch APIに関するよくある質問

QBatch APIは必ず24時間掛かりますか

A24時間待たされるという意味ではありません。OpenAIは24時間以内の完了枠を設定しており、より早く終了する場合があります。Claudeでは多くのBatchが1時間未満で終了すると案内されています。Geminiも目標時間は24時間ですが、大半のケースではより早く終了するとしています。ただし、実際の処理時間を保証されたリアルタイムAPIとして使わないことが重要です。

QBatch APIならRate Limitはなくなりますか

Aなくなりません。通常APIとは異なるBatch用の制限があります。OpenAIでは1 Batchのリクエスト数、Queued Prompt Tokens、Batch作成回数に制限があります。大量処理だから無制限に登録できるわけではありません。

QBatch結果は入力順に返りますか

A入力順を前提にしてはいけません。OpenAIとClaudeはいずれも、結果順序が入力順と異なる可能性を明記しています。必ずcustom_idなどの一意な識別子を付けて対応させます。

QBatchが期限切れになったら料金は掛かりますか

AOpenAIでは、期限切れになっても24時間内に完了していたリクエストについては料金が発生します。未完了のリクエストはキャンセルされます。Claudeではexpiredとなりモデルへ送信されなかったリクエストには料金が発生しません。そのため、Batch全体ではなく各Itemの結果とUsageを保存することが重要です。

Q毎日実行する処理もBatch APIにしたほうがよいですか

A結果を数時間以内に必要としないなら有力候補です。毎日深夜に記事を分類し、翌朝の管理画面へ反映する処理などはBatchと相性があります。一方、深夜処理でも5分以内の完了を業務上保証しなければならない場合は、通常APIや別の推論Tierを検討します。

まとめ

Batch APIは、通常APIの単なる大量送信版ではありません。

複数のAIリクエストをプロバイダー側の非同期キューへ渡し、即時性を手放す代わりに、低料金と高いスループットを得る仕組みです。

2026年8月時点ではOpenAI、Claude、Geminiの主要なBatch APIはいずれも、対応する通常APIの基本的なモデル料金に対して50%程度の料金設定を採用しています。

大量の商品説明生成、レビュー分類、過去データ分析、モデル評価、RAG用Embedding、夜間処理のように、ユーザーがその場で結果を待たない処理に向いています。

一方、チャット、リアルタイム検索、ストリーミング、インタラクティブなAIエージェントなど、即時応答が必要な処理には通常APIを使います。

実装時にはBatchそのものだけでなく、各リクエストへ一意なcustom_idを付け、Batch IDとItemの状態をDBへ保存します。

結果の順番を信用せずIDで照合し、一部だけ失敗した場合は失敗Itemだけを再Batchします。

通常APIとBatch APIを完全に別機能として作るのではなく、

共通ジョブ基盤
realtime → 通常API
background → Batch API

という共通のジョブ基盤を作っておけば、同じAI機能を用途に応じて安価なBatchへ移行しやすくなります。

Batch APIを使うべきか迷ったときは、「何件あるか」より先に「その回答をユーザーが今すぐ必要としているか」を確認することが、最も分かりやすい判断基準です。