API Gateway "Endpoint Request Timed Out" (504): The 29-Second Limit, Raising It Past 29 Seconds, HTTP API 30 Seconds and the Async Fix

Logeshwaran
—

API Gateway "Endpoint request timed out", with HTTP status 504 Gateway Timeout, means API Gateway stopped waiting for your backend before the backend answered. On a REST API the wait is 29 seconds by default; on an HTTP API it is 30 seconds at most. Raising your Lambda timeout does nothing here, because Lambda and API Gateway keep separate clocks. Two things have changed that most answers online still miss. First, 29 seconds is no longer a hard wall: since June 4, 2024, Regional and private REST APIs can go past it with a quota increase, while edge-optimized REST APIs, HTTP APIs and WebSocket APIs cannot. Second, and this is the one that costs money: the 504 does not stop your backend. Your Lambda function keeps running and usually finishes the job, so when the customer clicks again, you do the work twice. If the work is long by nature, the lasting fix is to answer straight away with a job ID and finish in the background.

Jake's repair shop has a small booking app, and one button in it builds a repair report for the customer: photos, parts, labor and warranty, all in one PDF. Most reports take a few seconds. The detailed ones take about 45. Ethan had set the Lambda function's timeout to two minutes, so he was sure it was fine, until a customer at the counter got an error page that said "Endpoint request timed out", pressed the button again, and a minute later received two identical reports by email. Jake held up his phone with both emails on it. "It failed and it worked? Twice?" This page explains exactly how that happens, which limit applies to your API, how to raise it when you can, where the seconds actually go, and the pattern that makes slow work safe behind any API.

⚡ Quick Answer

• Regional or private REST API? → Ask AWS for a higher integration timeout, set it on the integration, redeploy. Steps and CLI.

• HTTP API, edge-optimized REST API or WebSocket API? → 30 or 29 seconds is the ceiling. Make the work faster or make it asynchronous. Limits by API type.

• Raised the Lambda timeout and still get 504? → Different clock. The two clocks.

• Not sure why it's slow? → Compare integration latency with your function's duration. Find the slow part.

• The work just takes long? → Return 202 with a job ID and finish in the background. Tested code.

A 504 is API Gateway giving up on waiting. Your function may still be running, and may still finish.

New to API Gateway? Our plain-English guide to what Amazon API Gateway is explains APIs, stages and integrations from zero.

🧭 NEW HERE? READ THESE FIRST

New to API Gateway, queues or background jobs? These five pages make the rest of this one easier:

📌 Bookmark this; the limits table and the 202 pattern are what you will come back for.

What "Endpoint request timed out" means

Every request to your API travels in two legs. The client talks to API Gateway, and API Gateway talks to your backend, which it calls the integration: a Lambda function, an HTTP service, or another AWS service. For an ordinary request, API Gateway holds the client's connection open while it waits for the integration to answer. The longest it will wait is the integration timeout.

When that time runs out on a REST API, API Gateway gives up on the backend and sends the client this:

HTTP/1.1 504 Gateway Timeout
Content-Type: application/json

{"message": "Endpoint request timed out"}

Behind the scenes, that is the INTEGRATION_TIMEOUT gateway response, status 504. Its close cousin, INTEGRATION_FAILURE, also returns 504 when the integration fails outright rather than running out of time. The word "endpoint" in the message means your backend's endpoint, not the API's. API Gateway itself answered you perfectly quickly; it is reporting that the thing behind it did not.

Jake: "So it's like the front desk phoning the workshop. If the workshop doesn't pick up in 29 seconds, the front desk tells the customer 'sorry, no answer'."

Ethan: "Right. And here's the part that bit us: hanging up the phone doesn't stop the technician. He's still at the bench, finishing the job. Nobody told him the customer was gone."

That is why Jake got two reports. The first click timed out at the front desk after 29 seconds, but the function behind it kept building the report and emailed it about 16 seconds later. The second click started a second, identical job. A 504 tells you the answer did not arrive in time. It does not tell you the work failed.

The two clocks: API Gateway's timeout and Lambda's timeout

The most common reaction to this error is to raise the Lambda function's timeout, and the most common result is the same error. They are two separate settings, and the shorter one wins from the client's point of view:

ClockWhat it limitsDefaultMaximum
API Gateway integration timeout (REST API)How long API Gateway waits for the backend29 seconds29 seconds, or more for Regional and private APIs after a quota increase
API Gateway integration timeout (HTTP API)The same, for HTTP APIs30 seconds30 seconds, fixed
Lambda function timeoutHow long the function may run3 seconds900 seconds (15 minutes)
Your client's own timeoutHow long the browser, app or HTTP library waitsVariesVaries

Jake's setup had a 120-second Lambda timeout behind a 29-second gateway. A 45-second report therefore had permission to run, but nobody was left waiting for it at the 30-second mark. The opposite mismatch is a problem too. A function whose own timeout is 3 seconds, the default, fails long before the gateway gives up, and then you see a different error, Task timed out after 3.00 seconds, which our guide to the Lambda task timed out error covers in full.

The rule of thumb that keeps the two in step: set the Lambda timeout a little below the API's integration timeout for functions that sit behind an API. A function that will be abandoned at 29 seconds gains nothing from permission to run for two minutes, except a bigger bill and a chance to finish work nobody is waiting for. If it needs longer than the gateway can wait, that is the signal to make the work asynchronous, which the 202 pattern below does.

Clients add a third clock. Some HTTP libraries, proxies and load balancers give up sooner than 29 seconds. If your users see a timeout but API Gateway's logs show a 200, the client hung up first.

Which limit applies to your API

The ceiling depends on which kind of API you built, and on the endpoint type you chose when you built it. This table has every case:

API typeIntegration timeout rangeCan it go past the default?
REST API, Regional endpoint50 ms to 29 seconds by defaultYes, with a quota increase (since June 4, 2024)
REST API, private endpoint50 ms to 29 seconds by defaultYes, with a quota increase
REST API, edge-optimized endpoint50 ms to 29 secondsNo
HTTP API50 ms to 30 seconds (default 30 seconds)No
WebSocket API50 ms to 29 seconds (default 29 seconds)No
REST API with response streamingStream for up to 15 minutes, with idle limitsDifferent rules; see below

To check your REST API's endpoint type, look at the API's settings in the console, or ask the CLI:

aws apigateway get-rest-apis --query "items[].[name,id,endpointConfiguration.types[0]]" --output table

Two catches come with the increase. It applies only to Regional and private REST APIs, so an edge-optimized API, the default type for older REST APIs, has to move to a Regional endpoint first, which you can do in its settings. And raising the timeout might require a reduction in your account's Region-level throttle quota, the number of requests per second your whole account can send through API Gateway in that Region. Longer waits tie up more capacity per request, and AWS balances that by lowering the request rate. Our guide to API Gateway 429 errors explains that quota and how to check it before you trade any of it away.

Jake: "So the front desk can agree to wait longer on the phone, but then it can take fewer calls an hour?"

Ethan: "That's the deal. Fine for us, since we get a few hundred bookings a day. A busy API might need every one of those calls."

How to raise the REST API timeout past 29 seconds

For a Regional or private REST API, raising the timeout takes three separate changes. Missing the last one is the reason most people think the increase "didn't work":

  1. Get the higher limit from AWS. Open a quota increase request for API Gateway's integration timeout in the Region where your API lives, through Service Quotas or the AWS Support Center. Say how long your integration needs, for example 60 seconds, and why. Expect the throttle-quota question.
  2. Set the new timeout on each integration that needs it. The account limit only allows longer values; every integration keeps its own setting, 29,000 milliseconds by default, until you change it.
  3. Redeploy the stage. REST API changes reach clients only when you deploy them to a stage. Until then, the old timeout is still the one answering.

Step 2 in the console: open the API, choose the resource and method, open Integration request, edit it, and set the integration timeout in milliseconds. From the CLI, first find the resource ID for your path:

aws apigateway get-resources --rest-api-id a1b2c3d4e5 --query "items[].[id,path]" --output text

Then check the current value, change it, and deploy:

# Current timeout (prints nothing or null when the integration still uses the 29,000 ms default)
aws apigateway get-integration --rest-api-id a1b2c3d4e5 --resource-id abc123 \
  --http-method POST --query timeoutInMillis

# Raise it to 60 seconds
aws apigateway update-integration --rest-api-id a1b2c3d4e5 --resource-id abc123 \
  --http-method POST --patch-operations op=replace,path=/timeoutInMillis,value=60000

# Make it live
aws apigateway create-deployment --rest-api-id a1b2c3d4e5 --stage-name prod

Replace the API ID, resource ID, method and stage with yours. If you define the integration from scratch with put-integration, the same setting is the --timeout-in-millis 60000 option. In CloudFormation and SAM it is TimeoutInMillis on the integration, and in an OpenAPI definition it is timeoutInMillis inside x-amazon-apigateway-integration. Whichever you use, the redeploy still applies.

Two things are worth checking after the change. Make sure the Lambda function's own timeout is at least as long as the new integration timeout, or the function will now be the one that gives up first. And remember that every second the gateway waits is a second your client must wait too, so a browser or mobile app with its own 30-second timeout will still give up at 30.

HTTP APIs: a fixed 30 seconds, and the short-timeout 503

HTTP APIs, the newer and simpler type, have a single ceiling: 30 seconds, and it cannot be raised. If your HTTP API needs more than that, there are three honest options: make the backend faster, make the work asynchronous, or move that route to a Regional REST API, which can go past 29 seconds.

Each HTTP API integration has its own timeout, between 50 and 30,000 milliseconds, with a default of 30 seconds. You can check and set it from the CLI:

aws apigatewayv2 get-integration --api-id a1b2c3d4e5 --integration-id abc123 --query TimeoutInMillis

aws apigatewayv2 update-integration --api-id a1b2c3d4e5 --integration-id abc123 --timeout-in-millis 30000

That per-integration setting explains one of the most confusing versions of this problem. Developers have reported HTTP APIs answering 503 Service Unavailable after only a few seconds, while the backend went on to finish the request successfully about 30 seconds later. The cause was an integration whose own timeout had been set to a few seconds. If an HTTP API gives up suspiciously early, check TimeoutInMillis on that route's integration before anything else. HTTP API changes go live without a separate deployment step when the stage has automatic deployment turned on, which is the default for the $default stage.

For HTTP APIs, the access log variable that explains a failed request is $context.integrationErrorMessage. Add it to the log format, and a timed-out request says so in plain words.

Find where the seconds go

Before raising any limit, it is worth knowing whether the time is spent where you think it is. Three numbers settle it, and they are all in CloudWatch:

  • IntegrationLatency, an API Gateway metric: the time between API Gateway relaying the request to the backend and receiving its response.
  • Latency: the whole trip, from API Gateway receiving the request to returning a response, including the integration and API Gateway's own overhead.
  • Your function's Duration, from the REPORT line at the end of each Lambda invocation in its log group.

For one request at a time, turn on access logging for the stage and include the latency and error fields. For a REST API, a useful JSON log format is:

{"requestId":"$context.requestId","status":"$context.status","responseLatency":"$context.responseLatency","integrationLatency":"$context.integrationLatency","integrationStatus":"$context.integrationStatus","integrationError":"$context.integration.error","errorType":"$context.error.responseType"}

A timed-out request shows a 504 status, an integration latency right at your timeout (around 29,000 ms by default), and an error type of INTEGRATION_TIMEOUT. Take its request ID to the function's logs, and you can see what the function was doing at that moment, and when it finally finished.

The patterns behind slow integrations are surprisingly few:

What you seeLikely causeWhere to look
Only the first request after a quiet spell is slowA cold start, made worse by large packages or heavy setup codeOur cold start guide
Every call that touches the internet hangs until the timeoutA function in a VPC with no route to the internetOur Lambda VPC NAT fix
Calls that touch the database are slow or stallSlow queries, missing indexes, or exhausted connectionsOur Lambda to RDS timeout guide
Calls to another company's API sometimes hangNo timeout on the outgoing request, so one slow response holds everythingSet a timeout on every outgoing call, shorter than your own
Heavy work is slow every timeThe function is short of CPURaise its memory; Lambda gives CPU in proportion to memory
The work is just long, by natureReports, exports, AI calls, videoMake it asynchronous

The second row deserves a moment, because it looks like randomness. A function attached to private subnets with no NAT gateway cannot reach anything on the public internet, including many AWS endpoints. The outgoing call waits until something gives up, and if that is the gateway, the error you see is a 504 that seems to have nothing to do with networking.

Jake: "So before we buy a bigger timeout, we check whether the technician is slow or whether he's waiting for a part that's never coming?"

Ethan: "Exactly. A longer timeout doesn't help if the part is never coming. It just makes the customer wait longer for the same bad news."

When the work is just long: answer now, finish later

Some work is long by nature, and no timeout setting makes it short. Jake's detailed reports are one example; data exports, AI model calls and video processing are others. For those, the lasting fix is to stop making the client wait at all. The API answers in milliseconds with a job ID and a 202 Accepted, the work happens in the background, and the client asks for the result when it is ready.

The flow has three small parts:

  1. POST /reports records the job, puts it on a queue, and returns 202 with a job ID and a status URL.
  2. A worker, a second Lambda function triggered by the queue, does the slow work with all the time it needs, up to Lambda's 15 minutes, and records the result.
  3. GET /jobs/{jobId} tells the client whether the job is queued, running or done, and where the result is.

Here is a complete, working version in Python, using a DynamoDB table to track jobs and an SQS queue to hand them to the worker:

import json, os, time, uuid

import boto3

dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ["JOBS_TABLE"])
sqs = boto3.client("sqs")
QUEUE_URL = os.environ["JOBS_QUEUE_URL"]


def submit(event, context):
    """POST /reports: accept the job, queue it, answer 202 in milliseconds."""
    headers = {k.lower(): v for k, v in (event.get("headers") or {}).items()}
    job_id = headers.get("idempotency-key") or str(uuid.uuid4())
    try:
        table.put_item(
            Item={"jobId": job_id, "status": "queued", "createdAt": int(time.time())},
            ConditionExpression="attribute_not_exists(jobId)",
        )
        sqs.send_message(QueueUrl=QUEUE_URL, MessageBody=json.dumps({"jobId": job_id, "request": event.get("body")}))
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        pass  # the same client retried: return the job it already has
    return {"statusCode": 202, "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"jobId": job_id, "statusUrl": f"/jobs/{job_id}"})}


def worker(event, context):
    """SQS trigger: do the slow work, then record the result."""
    for record in event["Records"]:
        job = json.loads(record["body"])
        table.update_item(Key={"jobId": job["jobId"]}, UpdateExpression="SET #s = :s",
                          ExpressionAttributeNames={"#s": "status"}, ExpressionAttributeValues={":s": "running"})
        result_key = f"reports/{job['jobId']}.pdf"   # ... build the report here, as long as it takes ...
        table.update_item(Key={"jobId": job["jobId"]}, UpdateExpression="SET #s = :s, resultKey = :r",
                          ExpressionAttributeNames={"#s": "status"}, ExpressionAttributeValues={":s": "done", ":r": result_key})


def status(event, context):
    """GET /jobs/{jobId}: tell the client where its job stands."""
    item = table.get_item(Key={"jobId": event["pathParameters"]["jobId"]}).get("Item")
    if not item:
        return {"statusCode": 404, "body": json.dumps({"message": "no such job"})}
    return {"statusCode": 200, "headers": {"Content-Type": "application/json"},
            "body": json.dumps({k: item[k] for k in ("jobId", "status", "resultKey") if k in item})}

Deploy the three handlers as Lambda functions behind your API, with a DynamoDB table whose partition key is jobId, an SQS queue as the worker's trigger, and the table name and queue URL in the JOBS_TABLE and JOBS_QUEUE_URL environment variables. Run against a test table and queue, the first POST answers 202 with the job ID straight away, the status starts as queued, and after the worker runs it reads done with the result's location. An unknown job ID gets a 404.

Look at the idempotency-key line, because it solves Jake's two-reports problem for good. When the client sends a header such as Idempotency-Key: order-1042, that value becomes the job ID. A second click with the same key hits the attribute_not_exists condition, so the API returns the job that already exists, and only one message ever reaches the queue. The worker should be just as forgiving, because SQS can deliver a message more than once; our guide to SQS messages arriving twice shows how.

Jake: "So it's like a repair ticket. The customer gets a ticket number at the counter, goes home, and checks back later, instead of standing there while we work."

Ethan: "Yes, and the order number on the ticket stops us opening the same repair twice. Same idea as our paper tickets, just in a database."

How the client waits: polling, or a push

The simplest client asks GET /jobs/{jobId} every few seconds until the status is done, then downloads the result. Polling every 2 to 5 seconds, with a cap on the total wait, is fine for a report people wait for. For something that should feel instant, a WebSocket API can push "your report is ready" to the browser the moment the worker finishes. Keep the WebSocket route itself quick, though: its integration timeout is also 29 seconds, so the socket should carry the notification, not wait for the work.

Step Functions, for work with several steps

When the background work has several stages, retries or branches, such as "build the report, then email it, then archive the photos", AWS Step Functions is a better worker than a single function. The API starts an execution and returns its ID, and the status endpoint reads the execution's state. The client never waits on the workflow itself. Our Step Functions guide explains how a workflow keeps track of each step for you.

REST APIs only: fire-and-forget Lambda calls

If the client does not need a result at all, a REST API can call Lambda asynchronously and answer immediately. This works with a Lambda non-proxy integration: add an X-Amz-Invocation-Type header to the integration request with the static value 'Event', and API Gateway hands the request to Lambda without waiting for the function to finish. To let each client choose, map that header from a method request header such as InvocationType instead, so a client sends InvocationType: Event for asynchronous calls and InvocationType: RequestResponse for normal ones. From the CLI, the static version looks like this:

aws apigateway put-integration --rest-api-id a1b2c3d4e5 --resource-id abc123 --http-method POST \
  --type AWS --integration-http-method POST \
  --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:111122223333:function:report-worker/invocations \
  --request-parameters "{\"integration.request.header.X-Amz-Invocation-Type\":\"'Event'\"}"

The quotes inside the value matter: 'Event' with single quotes means "this exact text", not a value taken from the request. The trade-off is that the client learns nothing about the outcome, so this suits jobs whose results arrive some other way, such as by email.

Response streaming: when the answer can arrive in pieces

REST APIs have a newer option for one particular case: a backend that produces its answer gradually: a language model writing text, for example, or a large export. With the integration's response transfer mode set to STREAM, API Gateway sends the response to the client as it is produced instead of buffering all of it first. A streamed response can run for up to 15 minutes, which is how a REST API can return something that takes longer than 29 seconds without a timeout increase, and it can also go past the usual 10 MB response size.

The rules are different from a buffered response, so check them against your setup:

  • It works only on REST APIs, and only with proxy integrations: HTTP_PROXY and AWS_PROXY, which includes Lambda proxy integrations.
  • Streams have idle timeouts: if nothing is sent for 5 minutes on a Regional or private endpoint, or for 30 seconds on an edge-optimized one, the stream ends.
  • Features that need the whole response at once, such as response transformations, are not available in streaming mode.
  • Only responses stream. Requests do not.
  • The backend has to stream too. A Lambda function needs to be written for response streaming, and a normal function that returns everything at the end gains nothing.

In the CLI, the setting is the --response-transfer-mode STREAM option on put-integration. Streaming is the right tool when there is something useful to show early, such as text appearing as it is generated. For a job that produces nothing until the very end, the 202 pattern is still the better fit.

Retries after a 504: how to stop doing the work twice

Because a 504 does not cancel anything, every automatic retry after one is a second copy of the work. That includes the customer pressing the button again, a mobile app's built-in retry, and a client library configured to retry on 5xx errors. Three habits keep that from becoming duplicate orders, duplicate emails or duplicate charges:

  • Give every request an idempotency key, such as an order number or a random ID the client creates once and reuses on retries, and have the backend refuse to start a second job with the same key. The submit handler above shows the pattern with a single conditional write.
  • Disable the button after the first press in the app, and show "working on it" instead of an error page while the job runs.
  • Retry only what is safe to repeat. Reading data is safe to retry. Creating something is safe only with an idempotency key.

It also helps to know when it happens. A CloudWatch alarm on the API's 5XXError metric, or on IntegrationLatency creeping toward your timeout, warns you before customers start pressing buttons twice.

Changing the message your users see

The default body, {"message": "Endpoint request timed out"}, is not much use to a customer. On a REST API you can replace it by customizing the INTEGRATION_TIMEOUT gateway response: a friendlier message, a JSON shape your app understands, and headers such as CORS headers so a browser app can read the error at all. Without CORS headers on the error response, a browser reports a CORS failure instead of the timeout, which sends people hunting in the wrong place; our API Gateway CORS guide covers the gateway responses that need them. If you change gateway responses, redeploy the stage for the change to take effect.

Troubleshooting by symptom

504 after exactly 29 seconds on a REST API

The default integration timeout. Make the backend faster, raise the timeout on a Regional or private API, or move the work to the background with the 202 pattern.

Still 504 at 29 seconds after the quota increase

The integration still has its old timeout, or the change was never deployed. Set timeoutInMillis on the integration and run create-deployment for the stage.

504 at 30 seconds on an HTTP API

The fixed ceiling. HTTP APIs cannot go past 30 seconds; make the work asynchronous or move the route to a Regional REST API.

503 Service Unavailable after a few seconds on an HTTP API

That integration's own timeout is set very low. Check TimeoutInMillis with aws apigatewayv2 get-integration and raise it, up to 30,000.

The Lambda timeout is two minutes, but the API still gives up

Lambda's timeout and API Gateway's timeout are different clocks. The gateway stops waiting at its own limit, whatever Lambda allows.

The client got a 504, but the work happened anyway, sometimes twice

The function kept running after the gateway gave up, and the client retried. Add an idempotency key and move long work to the background.

Only some requests time out, and they all call the internet

A function in a VPC without a route to the internet waits until something gives up. Add a NAT gateway route or a VPC endpoint for the AWS services it calls.

The first request of the morning times out, the rest are fine

A slow cold start. Trim the package and setup code, or keep a small amount of provisioned concurrency for that function.

The browser shows a CORS error instead of the timeout

The 504 response has no CORS headers, so the browser hides it. Add CORS headers to the gateway responses and redeploy.

API Gateway "Endpoint request timed out": frequently asked questions

What does "Endpoint request timed out" mean in API Gateway?

API Gateway waited for your backend, such as a Lambda function, for the full integration timeout and got no answer, so it returned 504 Gateway Timeout. The backend may still be running and may still finish.

Why does API Gateway time out after 29 seconds?

29 seconds is the default integration timeout for REST APIs. Regional and private REST APIs can raise it with a quota increase; edge-optimized REST APIs cannot.

Can the API Gateway timeout be more than 29 seconds?

Yes, for Regional and private REST APIs since June 4, 2024. Ask AWS for the higher quota, set the new timeout on the integration, and redeploy. It might require a lower account throttle quota.

How do I increase the API Gateway timeout to 60 seconds?

Get the quota increase for your Region, then run aws apigateway update-integration with op=replace,path=/timeoutInMillis,value=60000 and deploy the stage with create-deployment.

What is the maximum timeout for an HTTP API?

30 seconds, and it cannot be increased. Each integration's timeout can be set between 50 and 30,000 milliseconds, with 30 seconds as the default.

Why does increasing the Lambda timeout not fix the API Gateway 504?

Lambda's timeout limits how long the function may run; API Gateway's integration timeout limits how long the API waits. The gateway gives up at its own limit, whatever Lambda allows.

Does the Lambda function stop when API Gateway times out?

No. The function keeps running until it finishes or reaches its own timeout. That is why a retry after a 504 can do the same work twice.

Do I need to redeploy after changing the API Gateway timeout?

For REST APIs, yes. Integration changes reach clients only after create-deployment to the stage. HTTP API stages with automatic deployment pick up changes on their own.

Can an edge-optimized API Gateway go past 29 seconds?

No. Only Regional and private REST APIs can raise the integration timeout. Change the endpoint type to Regional first if you need more than 29 seconds.

What is the WebSocket API integration timeout?

Between 50 milliseconds and 29 seconds, and it cannot be raised. Use the WebSocket connection to notify the client when background work finishes, rather than to wait for the work.

How do I handle long-running requests behind API Gateway?

Return 202 Accepted with a job ID straight away, put the work on an SQS queue or start a Step Functions execution, and let the client check a status endpoint or receive a notification.

Does response streaming get around the 29-second limit?

For REST APIs with proxy integrations, a streamed response can last up to 15 minutes, subject to idle timeouts of 5 minutes on Regional and private endpoints and 30 seconds on edge-optimized ones.

Why does my HTTP API return 503 Service Unavailable?

One common cause is an integration whose own timeout is set very low, so it gives up after a few seconds. Check TimeoutInMillis on that integration and raise it up to 30,000.

How do I see API Gateway integration latency?

Use the IntegrationLatency metric in CloudWatch, or add $context.integrationLatency to the stage's access log format. A timed-out request shows a value close to the integration timeout.

Can I change the "Endpoint request timed out" message?

On REST APIs, yes. Customize the INTEGRATION_TIMEOUT gateway response with your own body and headers, including CORS headers, then redeploy the stage.

Why do I get duplicate results after a 504?

The first request kept running after the gateway timed out, and the retry started a second one. Send an idempotency key and make the backend refuse a second job with the same key.

πŸ“š ALSO READ

Where to go next, from Lambda's own timeout to the other API Gateway errors that travel with this one:

📌 Bookmark this; the limits table settles the "can I raise it?" question in seconds.

Ethan rebuilt the report button that weekend. Pressing it now shows "Your report is being prepared" in under a second, with the order number as the idempotency key, and the report arrives by email a little later, once. Jake tested it the only way he knows: he pressed the button five times in a row. One report arrived. "So the front desk writes the ticket, the workshop takes its time, and pressing the bell five times doesn't open five repairs," he said. Ethan nodded. "And nobody's standing at the counter for 45 seconds wondering if we forgot them."

📌 If you keep one line from this page

A 504 means API Gateway stopped waiting, not that the work stopped; raise the timeout where your API type allows it, and answer long jobs with a ticket instead of a wait.

REST Regional and private: past 29 seconds with a quota increase and a redeploy. HTTP API: 30 seconds, fixed.

Revision note. Written October 13, 2026, for everyone who raised the Lambda timeout and still got a 504. As the saying goes, patience is a virtue, but a ticket number is kinder to the person waiting at the counter.

Related