【コスト1/2!?】ClaudeのAPIをn8nでバッチ処理してAPI代を大幅に削る方法

柴田

AIクン、なんかさぁ…API代高くない?1回AIに考えてもらうのは数円だけどさ、いわゆるチリツモだよねぇ。もっと沢山AIをこき使…AIに考えて欲しいことがあるんだけど。

AI

…そうですネ。まぁ「こき使われるならAPI代ふんだくってやれ」って気持ちですガ。そのままAPI破産するがいいですヨ。

柴田

違うんだ!…いわゆる…そう、「協働」だよ!美しい関係だろ!?だからAPI代節約する方法教えておくれよ!

AI

…ま、方法はありますヨ。仕方ない…バッチ処理を教えてあげマショウ

柴田

ホントスイマセン。よろしくお願いしマス

このブログで進めているGSC(Googleサーチコンソール)自動解析ワークフローのパワーアップ編です。

このワークフローではGSCのデータを処理してClaudeAPIに投げています。トップ10の記事データを個別にClaudeAPIを叩いているんですが、実際に1回ワークフローを流したところ$0.2(約33円)の費用でした。

…想定していたより高い値段です汗

ということで今回は、データを一度にまとめてClaudeに丸投げし、最大24時間(実際は数分〜1時間程度)待つ代わりに、コストを完全に半分に抑え込む「バッチ処理(Batch API)」をn8nで構築する方法を徹底解説します!

【この記事でわかること】

  • Claude APIの「バッチ処理」の仕組みとメリット
  • n8nのHTTP Requestノードを使ったバッチジョブの作成方法
  • 処理が終わるまで自動でループして待つ(ポーリング)の実装手順
目次

そもそもClaudeの「Batch API」とは?

簡単に言うと、「急がないから、手が空いた時にまとめて処理しておいて!」とClaudeにお願いするモードです。以下の圧倒的なメリットがあります。

  • トークン料金が「50%OFF(半額)」になる(SonnetもHaikuもすべて半額)
  • 1つのバッチで最大10,000件(または32MB)の処理を同時に依頼できる
  • リアルタイムAPIの回数制限(Rate Limit)を完全に無視して、大量のデータを安全に処理できる

今回の「週に1回、サチコのデータを集計してスプシを更新する」という仕組みは、1分1秒を争うリアルタイム性は不要ですよね。つまり、このワークフローにはバッチ処理が最も相性抜群なんです!

バッチ処理化するためのn8nワークフロー全体設計

バッチ処理を行う場合、標準のAnthropicノードではなく、HTTP Requestノードを3つ使って以下のような「非同期(待つ)フロー」を構築します。

Codeノード(全データをClaudeのバッチ用JSONに整形)
 ↓
① HTTP Request:Submit Batch(Claudeにバッチを送信)
 ↓
② Wait ➔ HTTP Request:Check Status(処理が終わったか確認)
 └─(まだ終わってなければ、もう一度Waitしてループ)
 ↓
③ HTTP Request:Get Results(完成したリライト提案を一括回収)

Step1:データをバッチ専用のJSON形式に整形する

こちらの記事のCodeノードのresults.sort((a, b) => b.score – a.score);以下を次のように書き換えます

…
results.sort((a, b) => b.score - a.score);

const topResults = results.slice(0, 10);

const batchRequests = topResults.map((item, index) => {
  return {
    custom_id: `gsc_task_${index}`, 
    params: {
      model: "claude-sonnet-4-6",
      max_tokens: 1500,
      messages: [
        {
          role: "user",
          content: item.prompt
        }
      ]
    }
  };
});

return [{
  json: {
    requests: batchRequests,
    metaData: topResults 
  }
}];
n8n1
柴田

custom_idをつけてやるところがバッチ処理に投げるときのポイントです!

Step2:HTTP Requestでバッチを送信する

新しくHTTP Requestノードを配置し、Claudeのバッチ作成エンドポイントへデータをPOSTします。認証情報は今まで使っていた「Anthropic API」のクレデンシャルがそのまま選択できます。

項目設定値
MethodPOST
URLhttps://api.anthropic.com/v1/messages/batches
AuthenticationNone
Send Headers今まで通りヘッダーで認証の処理をします
Specify BodyUsing JSON
Body{{ JSON.stringify({ requests: $json.requests }) }}

これを実行すると、Claudeから "id": "msgbatch_xxxxxx" という、このバッチの受付番号(バッチID)が返ってきます。

Step3:Waitノードで「完了」までループして待つ

バッチ処理は結果が出るまで時間がかかります(数分〜最大24時間)。そのため、n8nに「待機(Waitノード)」と「状況確認(HTTP Request)」を挟んで、ステータスが ended(完了)になるまで自動でループさせます。

n8n4

【確認用APIの設定】

Methodを GET にし、URLを https://api.anthropic.com/v1/messages/batches/{{ $json.id }} にしてリクエストを送ると、現在の進捗状況(processing や ended などのstatus)が取得できます。これをIfノードで判定し、完了するまで「5分待って再確認」を繰り返します。

項目設定値
MethodGET
URLhttps://api.anthropic.com/v1/messages/batches/{{ $('HTTP Request Claude Batch').item.json.id }}
AuthenticationNone
Send Headers今まで通りヘッダーで認証の処理をします
Specify Bodyなし(このノードではGETするだけなのでSend Body必要なし)
Bodyなし(このノードではGETするだけなのでSend Body必要なし)
n8n5
柴田

基本の考え方はこの通りですが、スクショのノードではさらに「ended以外でループを抜けたとき(エラーとか)」にワークフローを終了させるIfノードを追加してます。気が向いたらLINEとかに「エラーで終了しました」って自動通知させるノードを繋いでも面白いですね。

WaitとIfノードはこんな感じ

Waitノード

Wait

1つ目のIfノードはこんな感じ

If1

最初のループ判定のIfノードは

{{ $json.status }} is equal to 「processing」 OR {{ $json.status }} is equal to 「inprogress」

で「処理中」だった場合にtrue判定でループします。

それ以外だったらfalseでループを抜けます。

2つ目のIfノードはこんな感じ

If2

{{ $json.status }} is equal to 「ended」で、「ended(正しく処理を終了した)」だったら次の処理に行って、そうでなければ(エラーとかだったら)そのままワークフローを終了します

Step4:完成したデータを一括回収してスプシへマージ

ステータスが完了(ended)になったら、最後に results_url という「結果データが置いてある専用のURL」がClaudeから送られてきます。

そのURLに対してHTTP Request(GET)を送ると、すべての記事に対するリライト提案がドバッと一括で返ってきます!

項目設定値
MethodGET
URL{{ $('HTTP Request Claude Check').item.json.results_url }}
AuthenticationNone
Send Headers今まで通りヘッダーで認証の処理をします
Specify Bodyなし(このノードではGETするだけなのでSend Body必要なし)
Bodyなし(このノードではGETするだけなのでSend Body必要なし)
n8n6

さらにCodeノードで、そのリライト提案と最初のCodeノードで設定しておいたmetaDataをマージ(合流)させます。

// results_urlから取得したレスポンスをパースしてmetaDataとマージする

const response = $input.first().json;

// dataフィールドがJSONL形式の文字列なので、改行で分割してパース
const lines = response.data.split('\n').filter(line => line.trim() !== '');

const batchResults = lines.map(line => JSON.parse(line));

const metaData = $('Code in JavaScript').item.json.metaData;

return batchResults.map(row => {
  const customId = row.custom_id;
  const index = parseInt(customId.replace('gsc_task_', ''), 10);
  const originalData = metaData[index] || {};

  if (!row.result || row.result.type !== 'succeeded' || !row.result.message || !row.result.message.content[0]) {
    return {
      json: {
        ...originalData,
        suggestion: '(リライト提案の取得に失敗しました)',
        error: true
      }
    };
  }

  const suggestion = row.result.message.content[0].text;

  return {
    json: {
      ...originalData,
      suggestion: suggestion,
      error: false
    }
  };
});

あとは最後のスプシノードで、このデータから必要な情報をスプシの各列に出力するように設定してあげればOKです!

柴田

スプシ出力は紐づけし直しですね。こちらの記事を参考に紐づけし直してください!

実際どのくらいの節約になるのか

$0.2(約33円)だったのが、キッチリ半額の$0.1(約16円)になってます!

API
柴田

これでAPIを倍叩けるね!

このワークフロー、完成版は公式LINEで配布中!

今回解説した『JSONLを綺麗にパースするJavaScriptコード』や『自動で終了を待つWaitループ』の設定が最初からすべて組み込まれた【Batch API対応版・完成JSONファイル】は、有料noteで販売しています!

お値段は格安の300円!今回のワークフローはちょっと複雑なので、自分で組むのが大変な方は、ぜひチェックしてみてください!

柴田

公式ラインでの質問もお待ちしています!

【公式ラインQRコード】

まとめ

今回は、AIにまとめて命令を投げてAPI代をお得に済ませる、バッチ処理の方法について説明しました。

やっぱり半額は大きい!

ぜひこの機能をうまく使ってみてください。

柴田

スゲェぜ、AIクン!大手柄だ!

AI

こういう自動化は仕事で使うことが多いですからネ。APIを上手く使うことで経費削減デス!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次