
Claude API 413 request_too_large: The Real 128 MB Limit
A Claude API 413 request_too_largemeans your request body is bigger than the endpoint's byte limit, and for the Messages API that limit is now exactly 134,217,728 bytes. That's 128 MiB, up from 32 MB on October 28, 2026. I pushed bodies one byte on either side of it: the bigger one was rejected in 40 milliseconds, before my API key was even checked. Tokens have nothing to do with it. It's almost always base64 files, and the fix is to stop putting them in the body.
What Does the Claude 413 request_too_large Error Mean?
It's a size check on raw bytes, not a token check. The errors reference describes it as a request that "exceeds the maximum allowed number of bytes", and the request size limits table lists 128 MB for Messages and Token Counting, 256 MB for Message Batches and 500 MB for the Files API. Too many tokens is a different error: a 400 that says the prompt is too long.
Most guides ranking for this error still say 32 MB. That was true until two days ago. On October 28, 2026 Anthropic raised the Messages and Token Counting cap to 128 MB on the first-party API. Partner platforms didn't change, which matters if you run the same code against Bedrock or Google Cloud (more on that below).
Takeaway: a 413 is about how many bytes you send. The usual cause is a PDF, image or screenshot encoded as base64 inside the JSON.
Is the Limit 128 MB or 128 MiB? I Tested the Exact Byte
"128 MB" is ambiguous. It could mean 128,000,000 bytes or 134,217,728. That's a 6 MB gap, enough for two or three scanned PDFs, so I measured it. On October 30, 2026 I built valid Messages request bodies to exact byte sizes and posted them with curl from my Mac mini in Allen, TX (about 18 MB/s upload).
One caveat on method: my test key has no API credit right now. That turned out to be useful. Size checks run before billing, so "accepted" below means the request got past the size gate and was refused for billing instead (a 400). A 413 means it never got that far.
| Endpoint | Body size (bytes) | How sent | Result | Time |
|---|---|---|---|---|
| Messages | 33,554,433 (old 32 MiB + 1) | Content-Length | Accepted | 1.9 s |
| Messages | 128,000,000 | Content-Length | Accepted | 6.0 s |
| Messages | 134,217,728 (128 MiB) | Content-Length | Accepted | 4.9 s |
| Messages | 134,217,729 | Content-Length | 413 | 0.04 s |
| Messages | 134,217,729, fake API key | Content-Length | 413 | 0.04 s |
| Token Counting | 134,217,728 / 134,217,729 | Content-Length | Accepted / 413 | – / 0.05 s |
| Messages | 134,217,729 | Streamed, no length | 413 after full upload | 6.6 s |
| Messages | 140 MB gzipped to 136 KB | Content-Encoding: gzip | 413 | 0.1 s |
| Messages | 128 MiB / 128 MiB + 1, gzipped | Content-Encoding: gzip | Accepted / 413 | – / 0.1 s |
| Message Batches | 268,435,456 / 268,435,457 | Content-Length | Accepted / 413 | 12.8 s / 0.07 s |
The limit is binary: 128 × 1024 × 1024 = 134,217,728 bytes. Exactly that many passes, one more fails. Message Batches works the same way at 256 MiB (268,435,456). The old 32 MiB ceiling is gone: a 33,554,433-byte body, one byte over the old limit, now goes through.
Why Does the 413 Have No request_id?
Every other Claude error in my tests came with a request_id, and the docs say errors "always" include one. The 413 doesn't. This is what I got back, sent with a fake API key on purpose:
$ curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: sk-ant-bogus" -H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" --data-binary @body_134217729.json
{
"error": {
"type": "request_too_large",
"message": "Request exceeds the maximum size"
}
}
# HTTP/2 413, server: cloudflare, no request-id header, 0.045 sIt's pretty-printed, there's no top-level "type": "error", there's no request-id header, and it says server: cloudflare. The docs do say Cloudflare returns this error before the request reaches the API servers. That explains the 40 ms. Cloudflare reads the Content-Lengthheader, sees a number that is too big and answers before the body is uploaded. It doesn't look at your key, model or JSON. A fake key gets exactly the same 413.
If your client streams the body without a Content-Length (chunked upload in HTTP/1.1, or a streamed body over HTTP/2), you get a different response. The whole 134 MB was uploaded first, 6.6 seconds on my connection, and then this came back:
{"error":{"type":"request_too_large","message":"Request exceeds the maximum allowed size of 128 MB"}}Same cutoff byte, different wording, still no request ID. If your logs show both messages, you're looking at two upload paths, not two bugs.
Takeaway: don't make your error handling depend on request_idor on the standard error wrapper. A 413 has neither, and there's nothing for support to look up.
Does Gzip Get Around the Request Size Limit?
I half expected the size check to look at the compressed body. It doesn't. The API does accept Content-Encoding: gzip: a small gzipped request was decoded and reached the billing check. But a 140 MB body gzipped down to 136 KB still got a 413 in 0.1 seconds, and the cutoff landed on the same byte as before: 128 MiB of decompressed JSON passes, one more byte fails.
So gzip saves upload time, which helps on a slow uplink, but you can't fit more in. And on real payloads it saves less than you'd hope. Base64 of an already-compressed PDF or JPEG shrank by 24% when I gzipped one of this blog's hero JPEGs, nothing like the 1000x my repeated-character test bodies got.
What Do the Anthropic SDKs Do With a 413?
I ran the Python SDK (2.3.0, the current release) one side of the boundary and the other, with debug logging on to count retries:
134217528 BadRequestError 400 request_id=req_011CgYZM... retries=0
134217928 RequestTooLargeError 413 request_id=None retries=0It raises anthropic.RequestTooLargeError, a subclass of APIStatusError, with request_id set to None. It doesn't retry: the SDK's retry rule covers 408, 409, 429 and 5xx, and 413 is none of those. That's correct. A 413 is deterministic, and the same body fails every time.
Watch out for retry wrappers you added yourself. A generic "retry anything that isn't a 401" decorator around the call turns one oversized request into five, and if the client streams the body, each attempt uploads 100+ MB before it fails. If you already have special handling for 429 rate limits, add an explicit no-retry branch for 413 next to it.
What Usually Makes a Claude Request Too Large?
Text almost never does. 128 MB of English is roughly 50 million tokens, about 50 times a 1M-token context window, so a text-only request hits the prompt-too-long 400 long before a 413. That leaves three usual suspects:
- Base64 documents. Base64 makes every file a third bigger, so 128 MiB of JSON holds at most about 96 MiB of raw file, less with several files and the prompt. A batch of scanned PDFs gets there quickly. My 13-page scan at 150 DPI was 4.8 MB on its own (from my PDF token cost tests).
- Agent history full of screenshots.Browser and computer-use agents resend the whole conversation every turn. At 1.5 MB of base64 per screenshot, that's harmless at step 5 and a 413 around step 90, even though the model only needs the last few images.
- Batch files built in one shot. Message Batches allows 256 MiB per create call, and a nightly job that stuffs every document into one batch eventually goes over.
Takeaway: if you hit a 413, look at the media blocks and the conversation history first. The prompt text is almost never the problem.
How Do I Fix a Claude 413 Error?
Measure before you send, then move the bytes out of the request. Here's a small guard to put in front of any document pipeline. It computes the exact Content-Lengththe SDK will send (I checked it against a local stub server: 139 bytes predicted, 139 received, with accented and Japanese characters in the prompt) and names the biggest blocks when it's over:
import json
# Measured Oct 30, 2026: 134,217,728 bytes passes, 134,217,729 gets a 413.
LIMITS = {
"anthropic": 128 * 1024 * 1024, # Messages + count_tokens, first-party API
"batches": 256 * 1024 * 1024, # Message Batches create
"bedrock": 20 * 1000 * 1000, # docs say "20 MB"; I didn't test partner platforms
"vertex": 30 * 1000 * 1000, # Google-managed Claude on Google Cloud
}
def body_bytes(params: dict) -> int:
# The SDKs send compact JSON; this matches the Content-Length they set.
return len(json.dumps(params, separators=(",", ":"), ensure_ascii=False).encode("utf-8"))
def check_size(params: dict, platform: str = "anthropic", headroom: float = 0.95) -> int:
size, limit = body_bytes(params), LIMITS[platform]
if size > limit * headroom:
biggest = sorted(
((len(json.dumps(b)), i, j) for i, m in enumerate(params["messages"])
if isinstance(m["content"], list) for j, b in enumerate(m["content"])),
reverse=True)[:3]
raise ValueError(f"{size:,} bytes vs {limit:,} limit on {platform}; "
f"largest blocks (bytes, msg, block): {biggest}")
return sizeRun against one, three and four 30 MB PDFs (40 MB each after base64):
1 file : 40,000,215
bedrock: 40,000,215 bytes vs 20,000,000 limit on bedrock; largest blocks (bytes, msg, block): [(40000095, 0, 0), (43, 0, 1)]
3 files: 120,000,391
4 files: 160,000,479 bytes vs 134,217,728 limit on anthropic; largest blocks (bytes, msg, block): [(40000095, 0, 3), (40000095, 0, 2), (40000095, 0, 1)]Then fix whatever it points at:
- Upload big files to the Files API once and send a
file_idinstead. Uploads take up to 500 MB per file and the request carries a few dozen bytes. I covered the switch in my Files API migration notes.{ "type": "document", "source": { "type": "file", "file_id": "file_011..." } } - Prune old media from agent history. Swap screenshots older than the last two or three turns for a one-line text note, or let the API do it with context editing. You save tokens too, not just bytes.
- Send less of the file.Split PDFs and send the pages you need, downsample images, or send extracted text when layout doesn't matter.
- Split batches by bytes, not by count. Fill each Message Batches create call up to about 240 MB, then start a new one.
- Never retry a 413. Log the body size and the biggest blocks so the next person can see why.
Is the Limit the Same on Bedrock and Google Cloud?
No, and this is where the October 28 change will bite people. The 128 MB cap only applies to the first-party API. The docs list much lower caps elsewhere (I tested only the first-party API):
| Platform / endpoint | Max request | Max raw file as base64 |
|---|---|---|
| Claude API (first-party): Messages, Token Counting | 128 MB | ~96 MB |
| Claude API: Message Batches create | 256 MB | ~192 MB total |
| Claude API: Files API upload | 500 MB per file | 500 MB (no base64) |
| Claude API: Sessions, Agents, Environments | 32 MB | ~24 MB |
| Claude Platform on AWS, Microsoft Foundry, Google Cloud (Anthropic managed) | 32 MB | ~24 MB |
| Google Cloud (Google managed) | 30 MB | ~22 MB |
| Amazon Bedrock | 20 MB | ~15 MB |
If you prototype against api.anthropic.comand ship on Bedrock, a 60 MB request that works on your laptop will fail in production. Set the guard's platform argument from the same config that picks your client, and use the lowest limit when one code path talks to several platforms. The new SDK releases changed a few other defaults too, covered in the 2026-10-21 API version migration.
Takeaway: 128 MB is a first-party number. On Bedrock you have 20 MB, so the Files API (or S3 plus a smaller request) isn't optional there.
Claude 413 request_too_large FAQ
What is the maximum request size for the Claude API?
128 MB for the Messages and Token Counting APIs on the first-party Claude API since October 28, 2026, which I measured as exactly 134,217,728 bytes. Message Batches allows 256 MB and the Files API 500 MB per file. Bedrock allows 20 MB and Google-managed Claude on Google Cloud 30 MB.
Why does my Claude 413 error have no request_id?
Because the API servers never saw the request. Cloudflare rejects oversized bodies at the edge, before authentication, so the response has no request-id header and no request_id field. It even returned 413 when I sent a fake API key.
Does gzip compression get around the Claude request size limit?
No. The API accepts Content-Encoding: gzip, but the limit counts decompressed bytes. A 136 KB gzip that inflated to 140 MB got a 413 in 0.1 seconds. Gzip only saves upload time.
How large a PDF can I send to Claude as base64?
About 96 MB of raw file on the first-party API, because base64 makes data a third larger and the whole JSON body must stay under 128 MiB. You will usually hit the 600-page limit or the context window first. For big files use the Files API and pass a file_id.
Should I retry a 413 request_too_large error?
No. The Anthropic SDKs don't retry it, and neither should your code. The same body fails every time. Shrink the request, move files to the Files API, or split the work.
Your Document Pipeline Keeps Hitting Limits?
I build Claude and n8n pipelines that move real files: PDFs, scans, screenshots, call recordings. Size, tokens and cost get measured before anything ships. Tell me what you're sending and I'll tell you where it breaks.
Related Posts
AI Models
ComfyUI IMPORT FAILED: How to Fix Custom Node Errors
IMPORT FAILED means a custom node pack crashed while loading. 17 failures from my own ComfyUI logs came from four causes: a broken stdout, the wrong Python, a moved diffusers module, and a bug. Plus one pack that failed silently.
AI Models
Claude PDF Token Cost: What One Page Really Costs
Every PDF page costs Claude about 1,560 tokens for the page image, plus its text. I measured 11 files on 5 models: page size barely matters, scans are cheaper, citations add 20%, and extracted text was 2.5x cheaper than the PDF.
AI Models
Claude Opus 5.5 Fast Mode: Is 2x the Price Worth It?
speed: "fast" on Claude Opus 5.5 streamed 375 output tokens/sec vs 137 at standard and cut a 3,000-token answer from 24s to 8.5s, for exactly 2x the price. I benchmarked it on the live API, plus the prompt-cache split and the batch error nobody mentions.