◉CLAUDE LABJP
●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●API — The Messages API maximum request size grew from 32 MB to 128 MB (10/28)●SONNET 4.5 — Retires on the Claude API on 11/30, 31 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●API — In API version 2026-10-21, passing temperature, top_p or top_k returns a 400 error●NEW — I Put a Ten-Line Promise File at the To…●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●API — The Messages API maximum request size grew from 32 MB to 128 MB (10/28)●SONNET 4.5 — Retires on the Claude API on 11/30, 31 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●API — In API version 2026-10-21, passing temperature, top_p or top_k returns a 400 error●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-10-30Intermediate

The Messages API Limit Is Now 128MB, but Bedrock Still Stops at 20MB — Splitting Your Preflight Constants by Route

On 10/28 the Messages API maximum request size went from 32MB to 128MB, but only on the first-party API. Here is a small Python preflight checker that keeps per-route limits in one place and judges a base64 image bundle before you send it, with the output I got.

Claude API138request size413Bedrockpreflight5

I was reading the platform release notes from the top on a Friday afternoon when one line from 10/28 stopped me. The maximum request size for the Messages API and the Token Counting API had gone from 32MB to 128MB.

That sent me back to a small script I wrote a while ago, the one that bundles images and hands them to Claude. Its preflight check opens with 32 * 1024 * 1024, hard-coded. My first thought was that I could just change it to 128 and move on.

Then I opened the API overview, and it wasn't that simple. The increase applies to the first-party API only. Bedrock is listed at 20MB, Claude Models on Google Cloud at 30MB or 32MB depending on who manages it, and each route has its own number.

So the first thing I'd like to say is this: before you change the constant to 128, check which route your requests actually travel. What follows is how I checked, and the checker I ended up with.

What changed, and what didn't

This table is what I could confirm from the official pages, as of 2026-10-30.

Route / endpointMax request sizeChanged on 10/28?
First-party Messages / Token Counting128MBRaised from 32MB
First-party Message Batches256MBNot mentioned in the release note
First-party Files API500MBNot mentioned in the release note
First-party Sessions / Agents / Environments32MBNot mentioned in the release note
Bedrock20MBNot covered
Claude Models on Google Cloud (Google managed)30MBNot covered
Claude Platform on AWS / Google Cloud (Anthropic managed) / Microsoft Foundry32MB (Messages and Token Counting)Not covered

The release note says "raised" only for first-party Messages and Token Counting. Batches and the Files API don't appear in that entry. The overview shows their current numbers, but I couldn't tell whether those values are new or were always there.

Going over the limit returns a 413 request_too_large. On the first-party API the docs say Cloudflare returns it before the request reaches the API servers, so the model never reads a character.

Three things to check before touching the number

Before rewriting any constant, I now look at these in order.

  1. Which route does my request take? A direct Anthropic() client, a Bedrock client, or a gateway in between? If there's a gateway, its own limit applies too.
  2. Which endpoint am I sending to? Messages, Batches, or a Files API upload that I reference later. The limits differ.
  3. Am I sending images or PDFs inline as base64? Base64 inflates the data to roughly 4/3 of its original size. What counts against the limit is the whole inflated request, not the size of the files on disk.

The third one is where I misread numbers most easily, so the next sections use real figures.

A checker with per-route limits

The point is to answer one question before sending: can this bundle go through this route as it is? It returns OK, WARN or NG. All the limit numbers live in a single dictionary and nowhere else.

"""Preflight checker: which limit applies to my route?"""
import base64
import json
import math
import os
import sys
 
MB = 1024 * 1024
 
# Messages limit per route, in MB, from the API overview.
# These can change, so the numbers live only in this dict.
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:
    """Bytes an image/PDF takes when sent inline as base64 (JSON wrapper excluded)."""
    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": "Please classify these."})
    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)}")

A few reasons it's written this way:

  • It judges the assembled payload, not the files on disk. That way the base64 inflation, the text and the JSON wrapper are all counted.
  • WARN starts at 90% of the limit. I wouldn't trust a request that lands right at the line, since headers or a gateway might add a few bytes.
  • The route is a required argument. If it defaulted to the first-party API, a bundle meant for Bedrock would slip through silently.

What it printed

In a sandbox I made six files of 3,500,000 random bytes each and bundled them as images. They aren't real images, but for a size check that doesn't matter. Nothing was sent to the actual 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

The same five files are OK on one route and NG on another. Each file is about 3.3MB on disk, yet it takes about 4.45MB once encoded. Five of them come to 22.25MB, which is past Bedrock's 20MB.

Counting 3.5MB-equivalent images only, this is how many fit in one request:

RouteLimitApprox. images per request
First-party API128MB28
Google Cloud (Google managed)30MB6
AWS / Foundry and similar32MB7
Bedrock20MB4

What I noticed here is that I used to remember the rule as "stay under 32MB and you're safe." I never thought it meant a wall between the fourth and fifth image on Bedrock. You can't see where the wall is by editing one number.

Pitfalls after the limit goes up

I don't think a higher limit makes it good design to send big bundles as they are. This is closer to a rule of thumb than a measurement: the bigger a request is, the more you resend when it fails.

  • A retry sends the same bytes again. If a 100MB request is cut off midway, every retry pushes 100MB again.
  • If you reference the same images repeatedly, uploading once to the Files API feels like the better fit. It accepts up to 500MB. I haven't tested it for this article, so check the official docs before relying on it.
  • Per-image size and count limits may exist separately from the whole-request limit. That's outside what I checked. If you handle many images, Claude Vision API in Production — Implementation Patterns for Image Analysis, PDF Processing, and OCR is a good companion.

In my own case, the wallpaper app's category classification sends images a few at a time. I wrote about that in Automating Wallpaper Classification with Claude Vision API — Accuracy, Cost, and a Batch Job That Survives Interruptions. Back then I sized the batches by image count; now I think sizing them by bytes is safer.

Where this fits in a preflight

Size is one layer of a larger preflight. The idea of removing 400s and 422s before you call is in A Five-Layer Preflight Design for Claude API — How I Cut Hundreds of 400/422/529 Errors to Zero. If you add a size layer, the verdict function above drops in as it is.

Listing your routes takes less than ten minutes. Keep limit numbers in exactly one place, because they will change. I intend to keep to that on the next day a number moves.

A good first step is to search your scripts for hard-coded values like 32 * 1024 * 1024 and add a one-line comment saying which route that number was about.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⬡ API & SDK2026-10-07
Before You Switch Models, Replay Your Own Call Shapes Against the Candidate for a Few Tokens
Change a model ID and the first production request is where a 400 shows up. Here is a small replay script that checks your own call shapes against a candidate model with max_tokens 16 before you switch.
⬡ API & SDK2026-10-28
When web fetch fails, a 400 and a 200 point to different places
Does adding web fetch to tools return a 400, or a 200 with an error_code inside the result block? A two-layer triage across org settings, domain rules, and where a URL came from, plus a small Python helper whose branches I checked.
⬡ API & SDK2026-10-27
Three Places Python SDK 2.0.0 Breaks Your Code — A Checking Order Built on a Mocked Transport
Upgrading to Python SDK 2.0.0 changes behavior in three places: the temperature argument, the auth header, and double credentials. Here are the outputs I reproduced on a mocked transport, the fix for proxy setups, and the order I check in.