Figures on this page were verified 31 August 2026 against the providers' own documentation. Pricing, context windows and rate limits change without notice, so confirm any number against the provider before you rely on it. Tell us if something here is out of date.
Vision requests fail for two unrelated reasons that look similar: the HTTP request body is physically too big, or the images consume more of the context window than you have left. The first is about bytes and usually returns 413. The second is about tokens and returns 400. Resizing helps both, but only if you resize for the right constraint.
Bytes are not tokens
Base64 encoding inflates a file by roughly a third, so a 6 MB photo becomes about 8 MB of request body before any JSON overhead. That is a transport limit and has nothing to do with the model.
Separately, every image is converted into tokens that occupy the same context window as your text. A large image can cost well over a thousand tokens on its own, and a handful will crowd out the conversation history you assumed you had room for.
| Signal | Payload too large | Context exceeded |
|---|---|---|
| Status | 413, sometimes 400 | 400 |
| Message mentions | Size, bytes, payload | Tokens, context length |
| Trigger | One large file | Several images, or images plus long history |
| Fix | Compress, or send a URL | Downscale, send fewer images |
Resize before you send
Both providers downscale oversized images server-side anyway, so uploading a 24-megapixel original wastes bandwidth and buys no extra detail. Doing it client-side is faster, cheaper and predictable.
from PIL import Image
import base64, io
def prepare(path, max_edge=1568, quality=85):
im = Image.open(path)
im.thumbnail((max_edge, max_edge)) # preserves aspect ratio
if im.mode in ("RGBA", "P"):
im = im.convert("RGB") # JPEG cannot store alpha
buf = io.BytesIO()
im.save(buf, "JPEG", quality=quality, optimize=True)
return base64.standard_b64encode(buf.getvalue()).decode()Keep the longest edge around 1,500 pixels for general use. Below roughly 700 pixels, small text in screenshots stops being legible to the model, which produces confident wrong answers rather than an error. That failure is far more expensive to debug than a 413.
Send a URL instead of bytes
If the images already live somewhere publicly reachable, referencing them removes the payload problem entirely and shrinks the request to a few hundred bytes.
{
"role": "user",
"content": [
{"type": "text", "text": "What is the error in this screenshot?"},
{"type": "image_url",
"image_url": {"url": "https://example.com/shot.png", "detail": "low"}}
]
}The URL has to be fetchable by the provider, which rules out anything behind your VPN or a signed URL that has already expired. A short-lived pre-signed object-storage URL is the usual production answer.
Note the detail parameter. Setting it to low caps the image at a small fixed token cost regardless of its dimensions. For classification, layout questions or “which button is this”, low detail is both sufficient and dramatically cheaper.
Budget images like text
The mistake that causes repeat failures is treating images as free attachments. They are input tokens with a different encoder. If you send several per request, subtract their cost from your context budget before counting conversation history.
- Cap image count per request in your own code and reject the rest with a clear message, rather than discovering the ceiling through a provider 400.
- Downscale aggressively for bulk work. Halving the longest edge roughly quarters the pixel count, and the token cost with it.
- Drop old images from history first. When trimming a conversation they are the cheapest thing to remove and the least likely to be referenced again.
- Model the budget with the context budget planner, which shows what is left once fixed overhead is accounted for.
Related errors
If the message names tokens rather than bytes, you are really looking at context_length_exceeded, where the arithmetic and the fixes are set out in full. The complete map is in the LLM API error reference.
Frequently asked questions
What is the difference between a 413 and a context error on a vision request?
A 413 is about bytes: the HTTP body is too large, usually because base64 encoding inflated the file by about a third. A context error is about tokens: the images plus your text exceed the model window. Compression fixes the first, downscaling or sending fewer images fixes the second.
What size should I resize images to?
Around 1,500 pixels on the longest edge for general use. Below roughly 700 pixels, small text in screenshots stops being legible to the model, which produces confident wrong answers rather than an error, and that is far harder to debug than a rejected upload.
Do images consume context tokens?
Yes. Every image is encoded into tokens that occupy the same window as your text, and a large one can cost well over a thousand. Treating images as free attachments is the main reason vision requests start failing unpredictably as a conversation grows.



