Skip to content
BytePatterns

API Gateway REST API vs HTTP API: Features or Price

9 min readBytePatterns

API Gateway REST API vs HTTP API: HTTP APIs are cheaper and faster with JWT auth and CORS built in; REST APIs add usage plans, validation, caching and WAF.

API Gateway REST API vs HTTP API in one line: an HTTP API is the newer, cheaper and lower-latency way to put an HTTPS front door on Lambda or any HTTP backend, with JWT authorizers and CORS as configuration; a REST API is the older, fuller product, and it is the only one of the two with API keys and usage plans, request validation, mapping templates, response caching, AWS WAF, resource policies, private endpoints, canary releases and X-Ray tracing. The decision is a checklist: if nothing on it is REST-only, take the HTTP API. Features and pricing are from the API Gateway documentation listed at the end, as of October 2026; both lists change, so re-read the comparison page before committing.

NeedHTTP APIREST API
Lambda proxy, IAM auth, Lambda authorizer, custom domainyesyes
JWT/OIDC authorizer, CORS as config, automatic deploysyesno (Cognito authorizer, manual OPTIONS)
API keys and usage plans, request validation, mapping templatesnoyes
Caching, WAF, resource policies, private endpoint, canary, X-Raynoyes

The problem it solves

The lesson's Lambda is invoked by SQS. The moment a browser or a partner needs to call it over HTTPS, something has to terminate TLS, check who is calling, shape the request and hand it to the function. API Gateway offers two products for that job with overlapping names, and the console asks you to pick one before you have written a route. Picking REST by habit costs money and latency on every request; picking HTTP by default and then needing API keys for a partner means rebuilding the API.

The intuition

Both do the core job the same way. A route such as GET /orders/{id} maps to an integration, most often a Lambda proxy integration: API Gateway turns the HTTP request into a JSON event, invokes the function and turns its return value back into an HTTP response. Both support IAM authorization with SigV4-signed requests, Lambda authorizers for custom logic, custom domain names with ACM certificates, access logging and CloudWatch metrics.

HTTP APIs take the common case and make it cheap. They were designed for the proxy pattern: lower per-request price, lower added latency, a $default stage with automatic deployment so a route change is live without a deploy step, CORS configured once at the API level instead of a hand-written OPTIONS method per resource, and a JWT authorizer that validates tokens from any OpenID Connect issuer, including Cognito user pools, without a Lambda in the path. Private integrations reach an ALB, an NLB or AWS Cloud Map through a VPC link, which is how a container service behind a load balancer gets a managed API in front of it.

REST APIs carry the features you pay for. Usage plans and API keys throttle and meter per customer, which is the basis of a monetised or partner API. Request validation rejects malformed bodies against a JSON schema before the function runs. Mapping templates in VTL reshape requests and responses, so a legacy backend can be exposed with a different contract. A response cache serves repeated reads without invoking anything. Resource policies restrict who may call the API by account, VPC or IP; a private endpoint makes it reachable only from inside a VPC through an interface endpoint; an edge-optimized endpoint fronts it with CloudFront. Canary release settings send a percentage of traffic to a new deployment, and X-Ray traces each request through the gateway into the function. WAF attaches to a REST API stage for web ACL rules.

The event your code receives differs. A REST API, and an HTTP API configured for payload format 1.0, send an event with httpMethod, path and requestContext.identity. An HTTP API defaults to format 2.0, where the method sits in requestContext.http.method, the path in rawPath, and the response can be a bare JSON body that API Gateway wraps in a 200 for you. A handler moved between the two without noticing the shape reads None for the method.

So the decision is the checklist in the table, read bottom-up: start from "HTTP API", and move to REST only when a line in the lower two rows is a real requirement, not a nice-to-have.

Watch it run

The animation is the lesson's Lambda behind a queue. Upload events arrive in SQS, the first batch finds no warm environment and pays a cold start, a burst brings two more, and reserved concurrency of 3 caps the function while the rest waits in the queue. API Gateway replaces the queue with a synchronous caller: the same cold start lands on a user's request as latency, there is no queue to absorb a burst, and throttling shows up as a 429 at the gateway instead of messages waiting. That is why usage plans, which REST APIs have, and the gateway's own throttling matter in front of a function.

Lambda & Event-Driven Design

Step 1 of 11

Upload events land in a queue. Lambda runs a poller that reads the queue for you and invokes your function with batches.

The same interactive animation as the lesson — step through it with the controls.

The code

A toy model of the decision: the three feature sets from the documentation's comparison, and a function that answers "HTTP API" unless something on the list is REST-only. The lists are abridged and dated; the comparison page is the source of truth:

# toy model of the decision, from the documented feature lists as of October 2026
REST_ONLY = {"api keys and usage plans", "request validation", "mapping templates",
             "response caching", "AWS WAF", "resource policies", "private endpoint",
             "canary release", "X-Ray tracing", "edge-optimized endpoint",
             "Cognito user pool authorizer"}
HTTP_ONLY = {"JWT authorizer", "private integration to ALB, NLB or Cloud Map",
             "automatic deployments"}
BOTH = {"Lambda proxy integration", "IAM authorization", "Lambda authorizer",
        "custom domain", "CORS", "access logging"}

def choose(needs):
    """Toy model: HTTP API unless a needed feature exists only on REST APIs."""
    unknown = set(needs) - REST_ONLY - HTTP_ONLY - BOTH
    if unknown:
        raise ValueError(f"not in the feature lists: {sorted(unknown)}")
    rest_reasons = sorted(set(needs) & REST_ONLY)
    if rest_reasons:
        return "REST API", rest_reasons
    return "HTTP API", ["lower cost and latency; nothing here needs REST"]

cases = {
    "mobile backend":  ["Lambda proxy integration", "JWT authorizer", "CORS"],
    "partner API":     ["Lambda proxy integration", "api keys and usage plans", "request validation"],
    "internal only":   ["Lambda proxy integration", "IAM authorization", "private endpoint"],
    "public, cached":  ["Lambda proxy integration", "response caching", "AWS WAF"],
}
for name, needs in cases.items():
    kind, why = choose(needs)
    print(f"{name:15} -> {kind:9} {why}")
# mobile backend  -> HTTP API  ['lower cost and latency; nothing here needs REST']
# partner API     -> REST API  ['api keys and usage plans', 'request validation']
# internal only   -> REST API  ['private endpoint']
# public, cached  -> REST API  ['AWS WAF', 'response caching']

CORS sits in BOTH because a REST API can serve it too, through an OPTIONS method you configure yourself; only the HTTP API has it as a single setting. A Cognito user pool is reachable from an HTTP API through the JWT authorizer, so "Cognito" alone is not a reason for REST.

The two event shapes a Lambda proxy integration can deliver, and a handler that reads either. Format 1.0 is what REST APIs send; 2.0 is the HTTP API default:

# toy model of the two Lambda proxy event shapes: the fields a handler usually reads
EVENT_V1 = {"version": "1.0", "httpMethod": "GET", "path": "/orders/42",
            "queryStringParameters": {"expand": "items"},
            "requestContext": {"identity": {"sourceIp": "203.0.113.9"}}}
EVENT_V2 = {"version": "2.0", "rawPath": "/orders/42", "rawQueryString": "expand=items",
            "queryStringParameters": {"expand": "items"},
            "requestContext": {"http": {"method": "GET", "path": "/orders/42",
                                        "sourceIp": "203.0.113.9"}}}

def handler(event, context=None):
    if event.get("version") == "2.0":                 # HTTP API default
        method = event["requestContext"]["http"]["method"]
        path = event["rawPath"]
        ip = event["requestContext"]["http"]["sourceIp"]
    else:                                             # REST API, and HTTP API with 1.0
        method = event["httpMethod"]
        path = event["path"]
        ip = event["requestContext"]["identity"]["sourceIp"]
    return {"statusCode": 200, "body": f"{method} {path} from {ip}"}

print(handler(EVENT_V1)["body"])
print(handler(EVENT_V2)["body"])
print(sorted(set(EVENT_V1) ^ set(EVENT_V2)))          # the top-level keys only one shape has
# GET /orders/42 from 203.0.113.9
# GET /orders/42 from 203.0.113.9
# ['httpMethod', 'path', 'rawPath', 'rawQueryString']

The real events carry many more fields, headers, stage variables and the authorizer's claims among them; the keys that move are the ones shown.

The complexity

The costs are per request and per feature:

  • Per request, the HTTP API is cheaper and adds less latency; the documentation positions it as the lower-cost choice for proxy APIs. On a high-volume API the difference is a line item, not a rounding error.
  • Per feature, every REST-only capability is work the gateway does for you that you would otherwise do in the function: validating bodies, caching responses, metering keys. Moving that into Lambda costs invocations and code.
  • Operationally, an HTTP API's automatic deployment removes a step; a REST API's stages, deployments and canaries add control and the ceremony that comes with it.

Where it goes wrong

  • Choosing REST by habit. A plain Lambda proxy with JWT auth needs nothing REST offers and pays for it on every call.
  • Choosing HTTP and then needing API keys. Usage plans do not exist on HTTP APIs; the partner programme means a new REST API and a migration.
  • Assuming the event shape. A handler written for httpMethod returns errors behind an HTTP API in format 2.0. Read version, or pin the integration to 1.0.
  • Expecting WAF or a resource policy on an HTTP API. Neither attaches; restrict access with authorizers, or put a REST API or CloudFront in front.
  • Treating cold starts as the gateway's fault. Both products expose the function's init time as latency; the fix is on the Lambda side, as in AWS Lambda cold starts.

When it shows up in interviews

As the "how do clients reach the function?" step of a serverless design, and as a certification question that lists a requirement, API keys or request validation or a private endpoint, and asks which API type satisfies it. A strong answer defaults to the HTTP API, names the REST-only features that would change the decision, and knows the two payload formats. Where the compute itself should run is the earlier decision, in Lambda vs Fargate vs EC2; the resource and method conventions the routes should follow are in REST API design, and the function behind the gateway is the Lambda and event-driven design lesson.

How to say it in an interview

"I start with an HTTP API: it is the cheaper, lower-latency product, it fronts Lambda or an ALB, and it gives me JWT authorization and CORS as configuration with automatic deployments. I move to a REST API only for a feature it alone has, mainly API keys and usage plans for partners, request validation and mapping templates, response caching, WAF, resource policies or a private endpoint, canary releases and X-Ray. Both support IAM and Lambda authorizers. I check the handler reads the right payload format, 2.0 on HTTP APIs by default, 1.0 on REST."

Frequently asked questions

What is the difference between a REST API and an HTTP API in API Gateway?

Both front Lambda or HTTP backends over HTTPS. An HTTP API is cheaper and lower-latency, with JWT authorizers, CORS settings and automatic deployments. A REST API adds API keys and usage plans, request validation, mapping templates, caching, WAF, resource policies, private endpoints, canary releases and X-Ray tracing.

When should I use an HTTP API instead of a REST API?

When your API is a proxy to Lambda or an HTTP backend and needs none of the REST-only features: no per-customer API keys, no body validation or payload transformation at the gateway, no response cache, no WAF or resource policy, no private endpoint. Most new internal and mobile backends fit that description.

Does API Gateway HTTP API support API keys and usage plans?

No. API keys and usage plans, which throttle and meter calls per customer, are a REST API feature. An HTTP API can throttle at the stage and route level and authorize callers with JWT, IAM or a Lambda authorizer, but it cannot issue keys or attach quotas to them.

Sources