Skip to content

Body Size Limits on Vercel Functions for Large AI Payloads

9 min read · updated August 11, 2026

Vercel caps both the request body and the response body of a Vercel Function at 4.5 MB. For an AI workload that is a much smaller document than it sounds, because the encoding you reach for first costs a third of it — and because the same number applies in both directions, with one documented exception.

The number and the two errors

Vercel’s Vercel Functions Limits page states that the maximum payload size for the request body or the response body of a Vercel Function is 4.5 MB. Exceeding it on the way in returns a 413 with the error code FUNCTION_PAYLOAD_TOO_LARGE. Exceeding it on the way out is a different failure — Vercel’s knowledge-base guide on the limit documents the response-side error as a 500 with FUNCTION_RESPONSE_PAYLOAD_TOO_LARGE.

The asymmetry in status codes is worth noting because it changes how you find the problem. A 413 is unambiguous and arrives fast. A 500 on the way out arrives after the function has done all its work, successfully, at full cost — you have paid for the model call and the user has an error page.

Both figures from Vercel’s Vercel Functions Limits page, read 11 August 2026. This cap has been stable for a long time, but it sits at a platform boundary Vercel does not fully control, so confirm before designing an upload path to the byte.

Where a document actually lands

Almost nobody posts 4.5 MB of raw bytes. They post JSON containing a base64-encoded file, and base64 encodes every three bytes as four characters. So the raw file that fits is about three-quarters of the cap:

4.5 MB cap
  x 3/4          base64 expands by 4/3
= 3.375 MB       raw bytes, before anything else
  - JSON envelope, field names, escaping
~ 3.3 MB         a realistic ceiling for one file per request

That derivation assumes a single base64 field and negligible other content; it is arithmetic from the stated cap, not a measured figure. The practical consequence is that a 4 MB scanned PDF — an unremarkable size for a forty-page document — does not fit, and the developer reading “4.5 MB” had every reason to think it would.

Text is kinder. A 4.5 MB UTF-8 text body is on the order of four million characters, which is far more than any current context window will accept anyway — so for plain text the model’s limit binds long before Vercel’s does. The payload cap is a binary-and-multimodal problem: images, audio, PDFs.

The failure that arrives on turn nine

The version of this that reaches production is not a single large upload. It is a chat endpoint that posts the whole conversation on every turn, because that is what a stateless model API requires: there is no server-side session, so the entire message array is resent each time.

For text that is harmless — the context window binds first. For a conversation containing images it is a slow-motion outage. Each attached image stays in the array for every subsequent turn, and each is base64 in JSON, so the request body grows monotonically and crosses 4.5 MB after a handful of attachments. The endpoint works in testing, works for most users, and returns 413 to your heaviest ones. It then keeps returning 413 for that conversation forever, because the history only grows — the user cannot retry their way out of it, and from their side the app has simply broken.

Two things follow. First, measure the serialised body before you send it and fail with something meaningful rather than letting the platform produce a 413 with no context:

const body = JSON.stringify({ messages });
if (new Blob([body]).size > 4_000_000) {
  // trim oldest attachments, or upload and pass references
}

The 4 MB threshold there is deliberately below the 4.5 MB cap: headers and any transfer framing sit alongside the body, and you want the margin to be yours rather than discovered.

Second, and better, do not keep image bytes in the conversation at all. Upload each attachment once, keep a reference in the history, and resolve references to URLs at request time. That converts a body which grows with the number of attachments into one which grows with the number of characters, which is a limit the model will reach long before Vercel does.

The streaming exception

Vercel’s guide on bypassing the limit notes that streamed function responses do not have this limit. That is the single most useful sentence on the subject and it follows from the mechanism: the cap applies to a buffered response that the platform must hold in full before forwarding, and a streamed response is never held in full.

For a generation endpoint this means the response side of the cap is usually a self-inflicted problem. Returning Response.json(completion) after awaiting the whole answer puts you under the cap; forwarding the provider’s stream does not. Producing more than 4.5 MB of generated text in one response is unusual, but a function that returns a generated image as base64 in JSON will hit it routinely, and the fix is to return the bytes as a stream with the right content type rather than as a JSON string. If your stream is arriving all at once instead, see the buffered-streaming page — a buffered stream is a buffered response and the cap applies again.

The other size limits nearby

Three more of Vercel’s documented size limits get confused with this one, and they constrain different things:

  • Bundle size — 250 MB uncompressed, or 500 MB for the Python runtime, with large functions supporting up to 5 GB when fluid compute with Active CPU is enabled. This is your deployed code and its dependencies, not traffic. Vercel notes the 250 MB figure is enforced by AWS.
  • Edge runtime code size — 1 MB on Hobby, 2 MB on Pro, 4 MB on Enterprise, after gzip. This is the limit that actually is tiered by plan, and it is small enough that one heavy dependency breaches it.
  • Environment variables — 64 KB total per deployment, and no single variable may exceed it; Vercel notes edge functions and middleware are limited to 5 KB per variable. Not a body limit, but the same class of surprise.

Working around it

Vercel’s documented answers all share one shape: the bytes should not travel through the function.

  • Upload from the browser directly to storage. The client sends the file to a media host or to Vercel Blob without the function acting as a proxy, and then posts only the resulting key or URL to your function. The 4.5 MB cap never applies because the file never enters a function body.
  • Pre-signed URLs on the way back. For retrieving a large asset, Vercel recommends storing it in a dedicated media host and returning a pre-signed URL carrying its own access-control policy, rather than proxying the bytes through a function response.
  • Send the model a URL, not the bytes. Several provider APIs accept an image or document by URL. Where they do, the file travels from your storage to the provider and your function passes a few hundred bytes of JSON, which removes the cap, the egress and a large part of the latency in one change.
  • Chunk on the client for genuinely huge inputs. Split, summarise per chunk, then combine — remembering that each chunk is a separate invocation with its own duration budget, which is usually easier to satisfy than one large one.