金曜の昼、プラットフォームのリリースノートを上から順に読んでいて、10/28 の一行で手が止まりました。Messages API と Token Counting API の最大リクエストサイズが、32MB から 128MB に引き上げられた、という内容です。
手元の送信前チェックの先頭に 32 * 1024 * 1024 を直書きしていたのを、そこで思い出しました。画像をまとめて Claude に渡す小さなスクリプトで、書いたのはずいぶん前です。数字が古くなったと分かった瞬間は、そのまま 128 に書き換えれば済む話だと思いました。
ところが公式の API overview を開くと、話はそう単純ではありませんでした。上がったのは自社 API の値だけで、Bedrock は 20MB、Google Cloud 上のモデルは管理者によって 30MB か 32MB と、経路ごとに別の数字が並んでいたのです。
最初にお伝えしたいのは、定数を 128 に書き換える前に、自分のリクエストがどの経路を通るかを確かめてください、ということです。その確かめ方と、経路別に上限を持つチェッカーを、順に書き残します。
変わったものと、変わっていないもの
公式の記載を、私が確認できた範囲で表にしました。値はすべて 2026-10-30 時点のものです。
| 経路・エンドポイント | 最大リクエストサイズ | 10/28 の変更 |
|---|---|---|
| 自社 API の Messages / Token Counting | 128MB | 32MB から引き上げ |
| 自社 API の Message Batches | 256MB | リリースノートに記載なし |
| 自社 API の Files API | 500MB | リリースノートに記載なし |
| 自社 API の Sessions / Agents / Environments | 32MB | リリースノートに記載なし |
| Bedrock | 20MB | 対象外 |
| Claude Models on Google Cloud(Google 管理) | 30MB | 対象外 |
| Claude Platform on AWS / Google Cloud(Anthropic 管理)/ Microsoft Foundry | 32MB(Messages と Token Counting) | 対象外 |
リリースノートが「引き上げた」と書いているのは、自社 API の Messages と Token Counting の二つだけです。Batches と Files API の上限は、この変更の説明には出てきません。表の値は overview に載っている現在の数字ですが、「今回上がったのか、もともとその値だったのか」までは読み取れませんでした。
また、超えたときに返るエラーは 413 request_too_large です。自社 API では、リクエストが API サーバーに届く前に Cloudflare が返すと書かれています。つまり、モデルが一文字も読まないうちに弾かれます。
数字を直す前に確かめる3つのこと
定数を書き換える前に、私は次の三つを順に見ることにしました。
- 自分のリクエストはどの経路を通っているか。
Anthropic()を直接使っているのか、Bedrock 用のクライアントなのか、社内のゲートウェイ越しなのか。ゲートウェイを挟んでいる場合は、ゲートウェイ側の上限も効きます。 - どのエンドポイントに送っているか。 Messages なのか、Batches なのか、Files API にアップロードしてから参照しているのか。上限は別々です。
- 画像や PDF を base64 で inline 送信していないか。 base64 は元のバイト数の約 4/3 倍に膨らみます。上限と比べるのは元ファイルの大きさではなく、膨らんだあとのリクエスト全体です。
三つ目は、数字の読み違いがいちばん起きやすい場所です。次の節で実際の数字を出します。
経路別に上限を持つチェッカー
何を解決するかを先に言うと、「この束を、この経路にそのまま送ってよいか」を送信前に OK / WARN / NG で返すものです。上限の数字は辞書に集め、コードのほかの場所には書きません。
"""送信前に「どの上限が自分の経路に効くか」を判定する小さなチェッカー。"""
import base64
import json
import math
import os
import sys
MB = 1024 * 1024
# 経路ごとの Messages 上限(MB)。公式の API overview の値。
# 変わりうるので、数字はこの辞書にだけ置く
LIMITS_MB = {
"first-party": 128,
"bedrock": 20,
"google-cloud-google-managed": 30,
"platform-on-aws": 32,
"google-cloud-anthropic-managed": 32,
"foundry": 32,
}
def inline_size(path: str) -> int:
"""画像・PDF を base64 で inline 送信したときの実バイト(JSON の囲みは除く)。"""
raw = os.path.getsize(path)
return 4 * math.ceil(raw / 3)
def request_bytes(payload: dict) -> int:
return len(json.dumps(payload, ensure_ascii=False).encode("utf-8"))
def verdict(total: int, route: str, margin: float = 0.9) -> str:
limit = LIMITS_MB[route] * MB
if total > limit:
return "NG"
return "WARN" if total > limit * margin else "OK"
if __name__ == "__main__":
route = sys.argv[1]
files = sys.argv[2:]
content = []
for f in files:
with open(f, "rb") as fh:
data = base64.standard_b64encode(fh.read()).decode()
content.append({"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": data}})
content.append({"type": "text", "text": "分類してください"})
payload = {"model": "claude-sonnet-5", "max_tokens": 1024, "messages": [{"role": "user", "content": content}]}
total = request_bytes(payload)
print(f"route={route} files={len(files)} total={total / MB:.2f}MB limit={LIMITS_MB[route]}MB -> {verdict(total, route)}")なぜこう書いたかを、三点だけ補足します。
- 判定は元ファイルではなく、実際に組み立てた payload のバイト数で行います。 画像の base64 だけでなく、テキストや JSON の囲みも数えるためです。
- WARN の余白を 10% にしています。 上限ぴったりで通る・通らないを当てにすると、ヘッダーやゲートウェイの付加分で結果が変わるかもしれません。
- 経路名を引数で必ず指定させます。 省略時に自社 API を既定にすると、Bedrock 向けの束を黙って通してしまうからです。
実行して出た数字
サンドボックスで、乱数で埋めた 3,500,000 バイトのファイルを6つ作り、画像として束ねて試しました。中身は画像ではありませんが、サイズの判定だけを見るには十分です。実際の API には送っていません。
raw per file: 3500000 bytes
route=first-party files=5 total=22.25MB limit=128MB -> OK
route=bedrock files=5 total=22.25MB limit=20MB -> NG
route=foundry files=5 total=22.25MB limit=32MB -> OK
route=bedrock files=4 total=17.80MB limit=20MB -> OK
route=first-party files=6 total=26.70MB limit=128MB -> OK
見てほしいのは、同じ5枚の束が、経路によって OK にも NG にもなる点です。元のファイルは1つあたり約 3.3MB なのに、base64 にすると約 4.45MB 分を占めます。5枚で 22.25MB になり、Bedrock の 20MB を超えました。
3.5MB 相当の画像だけで数えると、1リクエストに入る枚数は経路ごとに次のようになります。
| 経路 | 上限 | 入る枚数の目安 |
|---|---|---|
| 自社 API | 128MB | 28枚 |
| Google Cloud(Google 管理) | 30MB | 6枚 |
| AWS / Foundry など | 32MB | 7枚 |
| Bedrock | 20MB | 4枚 |
ここで私は、一つ気づきました。以前の私は「32MB を超えなければ安全」と覚えていて、それが Bedrock では4枚目と5枚目のあいだに壁がある、という意味になるとは考えていませんでした。壁の位置は、数字を一つ書き換えるだけでは見えないのです。
上限が上がったあとの落とし穴
128MB に上がったからといって、大きな束をそのまま送るのが良い設計になるわけではない、と私は考えています。理由は経験則に近いのですが、一つのリクエストが大きいほど、失敗したときにやり直す量も大きくなるからです。
- 送ったあとに失敗しても、再送では同じバイトをもう一度送ります。 途中で切れた 100MB のリクエストを再試行すれば、そのたびに 100MB が流れます。
- 同じ画像を何度も参照するなら、Files API に一度上げるほうが筋が良いと感じています。 Files API は 500MB まで受け付けるので、inline で束ねるよりはるかに余裕があります。ただし、使い方の詳細は公式のドキュメントで確かめてください。この記事では検証していません。
- 画像そのものの枚数や大きさの制限は、リクエスト全体の上限とは別に存在するかもしれません。 その値は今回の確認の範囲外です。画像を大量に扱う場合は、Vision の実装パターンを扱ったClaude Vision API 実装パターン — 画像解析・PDF 処理・OCR を本番品質で動かすまでも合わせてご覧ください。
私の場合、壁紙アプリのカテゴリ分類で画像を何枚かずつ束ねて送っています。その経緯は壁紙アプリの一括カテゴリ分類を Claude Vision API で自動化した話 — 実運用で見えた精度と限界に書きました。あの頃は束の大きさを枚数で決めていましたが、いまはバイト数で決めるほうが安全だと考えています。
送信前チェックに足すと良いもの
サイズの判定は、プリフライト全体の一部です。400 や 422 を呼ぶ前に消す考え方はClaude API の 400/422/529 を“呼ぶ前”に消す5層プリフライト設計 — 月数百件の失敗呼び出しをゼロにした実装にまとめています。サイズの層を足すなら、今回のチェッカーの verdict をそのまま一層として差し込めます。
自分の経路を一覧にして書き出す作業は、10分もかかりません。上限の数字は、変わる前提で一か所にだけ置く。 この一文だけは、次に数字が変わる日にも守るつもりです。
まずは手元のスクリプトで 32 * 1024 * 1024 のような直書きを検索して、その数字がどの経路の話だったかを一行コメントで書き足すところから始めていただければと思います。