You are currently viewing ALB vs API Gateway: Pick the Limits You Can Live With

ALB vs API Gateway: Pick the Limits You Can Live With

The ticket said the reporting endpoint returns a 504 after about thirty seconds. The application log said the job finished in forty-one seconds and wrote a perfectly good response to a client that had already hung up. Nothing crashed, nothing scaled wrong, nobody deployed anything that week.

The integration timeout had been sitting in front of that API since the day someone created it. It only became a bug when a customer’s dataset got big enough to cross it.

That is the real shape of the ALB vs API Gateway decision. You’re not picking a feature list. You’re picking a set of hard limits, a billing curve and an authentication story, then living inside them. The failure mode that bites isn’t a bad Friday, it’s finding the constraint months later, once the thing in front of your app has become load-bearing.

Below: how each one bills you, which limits surface late, what changed recently on the load balancer side, and a decision procedure.

The old rule of thumb is shakier than it looks

The advice you find everywhere is: containers and instances behind an Application Load Balancer, serverless behind API Gateway. Wrong often enough to cost you. ALB takes Lambda functions as targets. API Gateway reaches containers through private integrations with an NLB, an ALB, or Cloud Map. Compute type barely narrows the field.

What separates them is who’s calling and what needs enforcing before a request reaches your code. A browser session against your own web app is a different problem from a partner’s server calling a documented endpoint with a key you issued and a quota you promised. One needs routing and a session. The other needs a contract.

Application Load Balancer: routing you own the position of

ALB is a Layer 7 router inside your VPC. You give it subnets and security groups, attach listeners, and write rules that match on host, path, header, query string, method or source IP, then forward, redirect, return a fixed response, or authenticate.

Where it wins

  • Long requests. The idle timeout is an attribute you set, defaulting to 60 seconds. Report generation and slow third-party calls become a number in Terraform instead of an architecture problem.
  • Big bodies and streaming. Against instance, IP and container targets there’s no small payload ceiling to design around, and WebSocket upgrades pass through.
  • Browser authentication. An HTTPS listener rule can run an authenticate-oidc or authenticate-cognito action before forwarding. The load balancer does the redirect dance, sets a session cookie, and hands your target the verified claims in a header. Your app reads a header instead of implementing OAuth.

Where it doesn’t

  • No per-client rate limiting. No API keys with quotas attached. Rate limiting means AWS WAF rate-based rules in front, counting requests per source over a window. Blunter than a per-consumer plan, and a separate bill.
  • No request validation or response caching. A malformed body reaches your handler and burns your compute. A cacheable GET is recomputed unless you put a CDN in front.
  • Lambda targets are constrained. Request body capped at 1 MB, response JSON capped at 1 MB, WebSocket upgrades rejected with a 400. If your function returns a large document, that ceiling arrives as a 502 in production, not an error at deploy time.
  • You pay for it while it sleeps. An hourly charge per load balancer whether or not a request arrives. Three environments, three floors.

Amazon API Gateway: the contract layer

API Gateway isn’t a load balancer with extra features. It’s a place to define what your API promises: which routes exist, who may call them, how often, and in what shape. That framing explains most of its trade-offs.

Choosing it is really two decisions, because REST APIs and HTTP APIs are different products under one name.

  • REST APIs carry the full management surface: API keys, usage plans, per-client throttling, request validation, response caching, AWS WAF integration, the private endpoint type, execution logs and X-Ray tracing.
  • HTTP APIs are the stripped-down, cheaper option. They add a built-in JWT authorizer, automatic deployments and Cloud Map private integrations. They offer none of the six management features listed above.

That second line is the one people get wrong. HTTP APIs aren’t “REST APIs but cheaper”, they’re a different product that happens to serve HTTP. Pick them to save money, then discover you need metered access, and you’re rebuilding usage plans in your own code.

Where it wins

  • You’re selling or governing access. API keys tied to usage plans, on REST APIs, are the only native answer to “customer B must not burn customer A’s capacity”.
  • Rejecting bad requests before they cost you. Request validation against a model means a malformed payload never invokes your function. On per-invocation compute that’s a security boundary and a cost lever at once.
  • Zero idle cost. No hourly floor, so a dev stage nobody calls is free. For genuinely spiky traffic this is often the whole argument, and there’s no network to design either.

Where it doesn’t

  • The integration timeout. The default is 29 seconds. It can be raised, but AWS notes that going beyond 29 seconds may require reducing your Region-level throttle quota. You’re buying request duration with request rate.
  • Payload ceilings. There’s a documented maximum payload size, well below what a file upload service wants. Large objects want presigned S3 URLs either way, but here the gateway forces that conversation early.
  • Cost scales with request count. Per request plus data transfer. A health check hammered by a monitoring tool shows up directly on the line item.
  • The account throttle is shared. The default steady-state rate and burst quota applies per Region across your APIs in that account. Raisable, but a shared ceiling by default rather than a per-API one.

What changed: the load balancer validates JWTs itself now

For years the clean argument for API Gateway on machine-to-machine APIs was token validation. If a partner’s service calls you with a bearer token, someone has to check signature, issuer and expiry, and you’d rather that not be a library duplicated in every service.

ALB does this natively now. A rule on an HTTPS listener can carry a jwt-validation action that runs before any routing action. The load balancer fetches signing keys from a JWKS endpoint you configure, checks the signature, and requires iss and exp. It also validates nbf and iat when present, and you can configure up to ten additional claims. Valid tokens forward unchanged. Invalid ones never reach your VPC.

This isn’t authenticate-oidc, which is built for humans in browsers and works by redirecting. JWT validation does no redirect. It inspects the token and returns a yes or no, which is what service-to-service traffic needs. Validation action first, routing action second, and order matters:

aws elbv2 create-rule 
  --listener-arn "$LISTENER_ARN" 
  --priority 10 
  --conditions Field=path-pattern,Values="/api/*" 
  --actions '[
    {
      "Type": "jwt-validation",
      "JwtValidationConfig": {
        "JwksEndpoint": "https://issuer.example.com/.well-known/jwks.json",
        "Issuer": "https://issuer.example.com"
      },
      "Order": 1
    },
    {
      "Type": "forward",
      "TargetGroupArn": "$TARGET_GROUP_ARN",
      "Order": 2
    }
  ]'

Two constraints first. Only RS256 is supported, and the JWKS endpoint has a maximum response size and key count. A provider publishing a large key set fails that check, and the failure mode is that requests stop reaching your targets. Fetch your provider’s JWKS document and look at it before committing.

So “we need token validation at the edge” is no longer by itself a reason to reach for API Gateway. What the gateway still owns is everything downstream of that yes or no: keys, quotas, per-consumer throttles, shape validation, caching.

How each one actually bills you

This is where the argument usually goes wrong, because people compare unit prices instead of curve shapes.

API Gateway is a slope with no floor: per request, plus data out. Zero traffic costs nothing, ten times the traffic costs roughly ten times as much.

ALB is a floor plus a stranger slope. An hourly rate per load balancer, plus capacity units measured across four dimensions at once:

  • New connections per second
  • Active connections per minute
  • Processed bytes per hour
  • Rule evaluations per second, where the first ten processed rules per request are free

You’re charged on the highest of the four in a given hour, not the sum. That mechanic explains most surprises. Clients that open a fresh connection per call can bill you on new connections while your bandwidth graph looks flat. A listener with dozens of path rules can bill you on rule evaluations while everything else idles.

Two details worth knowing: Lambda targets get a smaller processed-bytes allowance per capacity unit than instance, container and IP targets, so identical traffic consumes more units. Enabling mutual TLS reduces the active-connections allowance per unit.

Before modelling anything, look at what you already consume:

aws cloudwatch get-metric-statistics 
  --namespace AWS/ApplicationELB 
  --metric-name ConsumedLCUs 
  --dimensions Name=LoadBalancer,Value=app/my-alb/0123456789abcdef 
  --start-time "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)" 
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" 
  --period 3600 
  --statistics Sum Maximum

Run that against a representative week and you have the real input. For API Gateway, the equivalent is request count per stage. Across several accounts, a cost visibility tool like Vantage or CloudZero attributes this per environment faster than Cost Explorer, though disciplined tagging gets you most of the way for free.

Rough shape: low steady traffic favours API Gateway because you skip the floor. High-volume, byte-heavy, long-lived-connection traffic favours ALB because the per-request slope disappears. Chatty traffic with tiny payloads is where the curves cross, and that one is worth calculating.


A decision procedure for ALB vs API Gateway

Run these in order, stop at the first clear answer.

  1. Does any request legitimately run past about thirty seconds? If yes and you can’t make it asynchronous, ALB. Raising the gateway timeout is possible but trades against your regional throttle quota.
  2. Do you need per-consumer quotas or metered access? If yes, API Gateway REST APIs. Nothing else here gives you keys and usage plans natively.
  3. Do requests or responses carry large payloads? If yes, ALB with instance or container targets. Both options constrain size, but the Lambda-target and gateway ceilings arrive sooner.
  4. Is traffic occasional, spiky, or mostly zero? If yes, API Gateway. An hourly floor for a few thousand requests a day is real cost with no matching benefit.
  5. Is the caller a browser session against your own app? If yes, ALB, with an authenticate action if you need login. The gateway’s management features are dead weight here.
  6. Is the caller another service with a bearer token and nothing more? Either works now. Decide on cost curve and payload shape, because JWT validation stopped being the tiebreaker.

Still torn? Choose the limits you’d rather explain to a customer in a year. Usually ALB for applications, API Gateway for products. Whichever you pick, alarm on the constraint rather than the symptom: integration latency approaching the timeout, or consumed capacity units trending up. Grafana Cloud or plain CloudWatch alarms both do the job. Then send a payload just over your ceiling in staging and confirm what the client actually receives.

Arguments that don’t survive contact

“It’s serverless, so it’s cheaper”

Per-request billing is cheaper at low volume and more expensive at high volume. Arithmetic, not philosophy. The crossover depends on your request size and connection reuse, which is why the capacity-unit metric matters more than anyone’s price table.

“We’ll put API Gateway in front of it later”

You can, through a private integration, and it’s a legitimate pattern. But “later” means a second hop, a second bill, a second set of timeouts and a second place to look during an incident. Adding it deliberately is fine. Assuming it’s free is not.

“ALB has no rate limiting, so it’s insecure”

ALB has no native rate limiting, but WAF rate-based rules in front of it are a real control, and a source-based limit is often closer to what you wanted than a per-key quota. Cloudflare in front of the load balancer is another common answer when you already want its caching and bot controls. What ALB genuinely lacks is the per-consumer contract, which matters when consumers are customers and doesn’t when they’re your own front end.

Check the pairing before you rely on it, though: WAF associates with ALB and with API Gateway REST APIs, but not with HTTP APIs. And the amount of request body WAF reads is configurable for some resource types and fixed for the load balancer. Anything past that window isn’t scanned.

“The front door has to be an AWS service”

For one service with predictable traffic, a plain VPS running Nginx or Caddy does the same job with far less surface. Providers like Contabo or InterServer are reasonable homes for a small internal API. It won’t scale into a multi-service platform and it moves patching and certificate renewal onto you, but “which AWS service” is sometimes the wrong question.

Frequently asked questions

Can I use API Gateway and ALB together?

Yes. API Gateway supports private integrations with an Application Load Balancer, so the gateway handles keys, quotas and validation while the load balancer routes inside the VPC. Sound pattern for a public product API in front of an existing internal platform. It also doubles the hops, the bills and the places a request can die, so use it when you need both jobs done.

Which is cheaper, ALB or API Gateway?

Neither, universally. API Gateway has no idle cost and charges per request, so it wins at low or intermittent volume. ALB charges an hourly floor plus capacity units billed on the highest of four dimensions, so it wins once the per-request slope dominates. Pull your consumed capacity units and request counts and do the arithmetic on your own traffic.

Can ALB do rate limiting?

Not natively. You get it by associating AWS WAF with the load balancer and using rate-based rules, which limit by source over a time window. If you need per-key quotas, that’s an API Gateway REST API usage plan, or logic you write yourself.

Does ALB support JWT authentication now?

Yes, through a jwt-validation action on an HTTPS listener rule. It verifies the signature against a JWKS endpoint and checks issuer and expiry, plus optional additional claims. Note the RS256-only restriction and the JWKS size and key count limits before designing around it.

Should I use HTTP APIs or REST APIs?

REST APIs if you need any of: API keys, usage plans, per-client throttling, request validation, response caching, WAF integration, or a private endpoint type. HTTP APIs when you need none of those and want the built-in JWT authorizer at a lower price. Migrating between them isn’t a config flag, so decide before you publish a URL.

The one thing worth remembering

ALB vs API Gateway isn’t a question about serverless versus containers, and it stopped being a question about token validation. It’s a question about whether the thing in front of your application needs to enforce a contract with people outside your organisation.

If it does, take the gateway and accept its timeout and payload ceilings as the price of keys, quotas and validation. If it doesn’t, take the load balancer and spend the saved effort on the network and the health checks. Either way, write down the three numbers that will eventually bite: timeout, payload ceiling, throttle quota. The 504 in that opening ticket wasn’t a bug. It was a decision nobody had written down.


Need a second opinion on your front door?

Cheap to get right at the start, expensive to revisit. If you’re staring at this now, or already living with the wrong answer:

  • Modelling ALB capacity units against API Gateway request counts on your actual traffic, so the comparison uses your numbers
  • Auditing an existing gateway or load balancer for the limits you’re closest to hitting: timeouts, payload ceilings, throttle quotas
  • Moving browser authentication off your application onto an ALB authenticate rule, or setting up JWT validation at the listener for service-to-service traffic
  • Designing the gateway plus private integration pattern when you need both layers, including where to terminate TLS and how to preserve client IPs
  • Building the Terraform or OpenTofu modules for either option, with listener rules, target groups, health checks and alarms in one place
  • Setting up alarms that fire on approaching limits rather than the 5XX that follows

Send me a listener rule dump, a stage configuration, or a week of consumed capacity unit metrics and I’ll tell you what I see.