Node.js Serverless on AWS Lambda · HTTP APIs on Lambda
Payload, Timeout and Streaming Limits
Why a report export works for small customers and fails with 413s and timeouts for big ones, and how to design around Lambda's limits.
Scenario
Example app: an analytics API has an endpoint that builds a CSV export of a customer's events and returns it in the response body.
What you see in production
Small customers download exports fine. Large customers get HTTP 413 or 502 errors ("response body too large") or 504 timeouts after about 30 seconds, although the function's own timeout is set to 5 minutes.
The naive code
export const handler = async (event) => {
const rows = await loadAllEvents(event.pathParameters.customerId); // could be millions
return { statusCode: 200, headers: { "content-type": "text/csv" }, body: toCsv(rows) };
};
Reproduce it
const LIMITS = { responseMb: 6, apiTimeoutS: 29 }; // buffered response limit; typical API Gateway integration timeout
const customers = [
{ name: "small", rows: 20_000, bytesPerRow: 120, rowsPerSecond: 50_000 },
{ name: "medium", rows: 400_000, bytesPerRow: 120, rowsPerSecond: 50_000 },
{ name: "large", rows: 3_000_000, bytesPerRow: 120, rowsPerSecond: 50_000 },
];
for (const c of customers) {
const mb = (c.rows * c.bytesPerRow) / 1_048_576;
const seconds = c.rows / c.rowsPerSecond;
const ok = mb <= LIMITS.responseMb && seconds <= LIMITS.apiTimeoutS;
console.log(`${c.name.padEnd(6)} ${mb.toFixed(1).padStart(6)} MB ${seconds.toFixed(0).padStart(4)} s ->`,
ok ? "inline response OK" : "too big or too slow: use S3 + presigned URL (async job)");
}
Output
small 2.3 MB 0 s -> inline response OK medium 45.8 MB 8 s -> too big or too slow: use S3 + presigned URL (async job) large 343.3 MB 60 s -> too big or too slow: use S3 + presigned URL (async job)
Root cause
Several limits apply, and the smallest one wins: Lambda's buffered request and response payload limit (6 MB each for synchronous invocations), API Gateway's integration timeout (29 seconds by default for REST APIs, 30 seconds maximum for HTTP APIs), and the function's own timeout (up to 15 minutes). A long function timeout does not help if the gateway gives up first. Building the whole file in memory also risks running out of memory.
The fix
Design large or slow operations asynchronously and move bytes through S3:
POST /exportsvalidates the request, writes a job record and enqueues work (SQS or Step Functions), returning202 Acceptedwith a job ID.- A worker function streams rows from the database into an S3 multipart upload, never holding the whole file in memory.
GET /exports/{id}returns the status and, when ready, a short-lived presigned S3 URL for the client to download directly.
For uploads, give clients presigned PUT URLs so large files never pass through Lambda. For moderately large responses that must be inline, response streaming (function URLs and supported integrations) lets a function send data progressively beyond the buffered limit.
Trade-offs
- Asynchronous jobs add polling or notifications (webhooks, WebSockets) and more states to handle.
- Presigned URLs are bearer credentials: keep expiry short and scope them to one object.
- Some API Gateway timeouts can be raised for certain API types at the cost of throttle capacity; prefer async design over long synchronous calls.
Detect it in production
- Alarms on 413, 502 and 504 responses per route; the
IntegrationLatencymetric approaching the timeout. - Function
DurationandMax Memory Usedtrending towards their limits for specific customers. - Size the largest tenants' data regularly; limits are usually discovered by the biggest customer.
Key points
- The smallest limit wins: payload size, gateway timeout or function timeout.
- Long or large work: async job + S3 + presigned URL.
- Stream instead of buffering; presigned uploads keep big files out of Lambda.
Further reading: Lambda quotas · Response streaming for Lambda functions · Sharing objects with presigned URLs
Practice questions
A function has a 5-minute timeout, but API clients receive 504 after about 30 seconds. Why?
Answer
API Gateway's integration timeout (29 seconds by default for REST APIs, at most 30 seconds for HTTP APIs) is shorter than the function's timeout, so the gateway gives up first. Make long work asynchronous instead.
How should an API return a 500 MB export generated by Lambda?
Answer
Not inline. Run the export as an asynchronous job that streams the file into S3, then return a short-lived presigned URL so the client downloads directly from S3.
What does Lambda response streaming change?
Answer
The function can send the response progressively instead of buffering it, improving time to first byte and allowing responses beyond the buffered payload limit, through function URLs and supported integrations.
Open in the app to mark lessons complete, take notes and track your progress.