Skip to main content
AWSServerlessintermediate

API Gateway Complete Guide

Build and manage APIs with AWS API Gateway, covering REST, HTTP, and WebSocket APIs with Lambda integration, caching, and custom domains.

CloudToolStack Team24 min readPublished Feb 22, 2026

Prerequisites

API Gateway Overview

Amazon API Gateway is a fully managed service for creating, publishing, maintaining, monitoring, and securing APIs at any scale. It acts as the front door for applications to access backend services, including Lambda functions, EC2 instances, ECS containers, or any publicly accessible HTTP endpoint. API Gateway handles all the undifferentiated heavy lifting of API management: request routing, throttling, caching, authentication, request/response transformation, CORS, SSL termination, and API versioning.

API Gateway supports three API types, each designed for different use cases: REST APIs (the original, feature-rich option), HTTP APIs (a newer, faster, cheaper option for simple proxying and Lambda integration), and WebSocket APIs (for persistent bidirectional communication). Choosing the right type is one of the most impactful architectural decisions you will make, as it affects cost, performance, and available features.

At scale, API Gateway can handle hundreds of thousands of concurrent API connections and millions of API calls per second. It integrates natively with AWS WAF for web application firewall protection, CloudWatch for monitoring, X-Ray for distributed tracing, and IAM/Cognito/Lambda authorizers for authentication. Every API deployed through API Gateway is automatically distributed across multiple availability zones for high availability.

API Gateway is Regional

API Gateway deployments are regional by default. For global APIs, pair API Gateway with CloudFront (REST APIs get this automatically with edge-optimized endpoints) or deploy to multiple regions with Route 53 latency-based routing. HTTP APIs are always regional and require explicit CloudFront distribution for edge caching.

REST API vs HTTP API vs WebSocket API

The choice between REST APIs and HTTP APIs is one of the most common questions when starting with API Gateway. HTTP APIs were introduced in 2019 as a simpler, faster, and cheaper alternative to REST APIs. They cover the most common use cases (Lambda proxy, HTTP proxy, JWT authorization) at up to 71% lower cost. However, REST APIs offer features that HTTP APIs lack, and for certain use cases, only REST APIs will do.

FeatureREST APIHTTP APIWebSocket API
Cost (per million requests)$3.50$1.00$1.00 + $0.25/million connection min
LatencyHigher (~30ms overhead)Lower (~10ms overhead)Low (persistent connection)
Lambda integrationProxy & customProxy only (v2 payload)Proxy
Request transformationVTL mapping templatesParameter mapping onlyRoute selection expression
Response transformationVTL mapping templatesNoNo
CachingBuilt-in (0.5 GB – 237 GB)No (use CloudFront)No
API keys & usage plansYesNoNo
Request validationYes (JSON Schema)NoNo
WAF integrationYesNoYes
Resource policiesYesNoNo
Private endpointsYes (VPC endpoint)Yes (VPC link)No
Edge-optimized endpointYesNoNo
Mutual TLSYesYesNo
Auth optionsIAM, Cognito, Lambda authorizerIAM, JWT (native), Lambda authorizerIAM, Lambda authorizer

When to Choose Which

Use HTTP API as the default choice for new projects, as it covers most use cases at lower cost and latency. Use REST API when you need request/response transformation (VTL), built-in caching, API keys with usage plans, request validation, WAF integration, or resource policies. Use WebSocket API for real-time bidirectional communication (chat apps, live dashboards, gaming, collaborative editing).

Lambda Integration Patterns

The most common backend for API Gateway is AWS Lambda. API Gateway supports two Lambda integration types: proxy integration (API Gateway passes the entire request to Lambda and returns the Lambda response directly) and custom integration (REST API only, where you define request/response transformations using VTL mapping templates).

Proxy integration is simpler and more commonly used. Lambda receives the full request context (headers, query parameters, path parameters, body) and returns a structured response with statusCode, headers, and body. The Lambda handler is responsible for all request parsing and response formatting.

REST API Proxy Integration (v1 Payload)

lambda-rest-api.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  console.log("Request:", JSON.stringify({
    method: event.httpMethod,
    path: event.path,
    queryParams: event.queryStringParameters,
    pathParams: event.pathParameters,
  }));

  try {
    const userId = event.pathParameters?.userId;
    if (!userId) {
      return {
        statusCode: 400,
        headers: {
          "Content-Type": "application/json",
          "Access-Control-Allow-Origin": "*",
        },
        body: JSON.stringify({ error: "userId is required" }),
      };
    }

    // Business logic here...
    const user = { id: userId, name: "Jane Doe", email: "jane@example.com" };

    return {
      statusCode: 200,
      headers: {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
        "Cache-Control": "max-age=300",
      },
      body: JSON.stringify(user),
    };
  } catch (error) {
    console.error("Error:", error);
    return {
      statusCode: 500,
      headers: {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*",
      },
      body: JSON.stringify({ error: "Internal server error" }),
    };
  }
};

HTTP API Proxy Integration (v2 Payload)

lambda-http-api.ts
import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";

export const handler = async (
  event: APIGatewayProxyEventV2
): Promise<APIGatewayProxyResultV2> => {
  // HTTP API v2 payload format is simpler and flatter
  console.log("Request:", JSON.stringify({
    method: event.requestContext.http.method,
    path: event.requestContext.http.path,
    queryParams: event.queryStringParameters,
    pathParams: event.pathParameters,
    sourceIp: event.requestContext.http.sourceIp,
  }));

  const userId = event.pathParameters?.userId;

  // HTTP API supports simplified response format
  // Just return an object - API Gateway wraps it in a 200 response with JSON content-type
  return {
    statusCode: 200,
    headers: {
      "Content-Type": "application/json",
      "Cache-Control": "max-age=300",
    },
    body: JSON.stringify({
      id: userId,
      name: "Jane Doe",
      email: "jane@example.com",
    }),
  };
};

Payload Format Differences

REST API (v1 payload) and HTTP API (v2 payload) send different event structures to Lambda. The v2 format is flatter and includes HTTP method and path underrequestContext.http instead of top-level httpMethod andpath. Multi-value query strings and headers are handled differently too. If you use a framework like Powertools for AWS Lambda, it abstracts these differences. If you write raw handlers, make sure you target the correct event type.

Request & Response Transformations

REST APIs support request and response transformations through Velocity Template Language (VTL) mapping templates. These templates let you reshape the request before it reaches your backend and reshape the response before it reaches the client. This is useful for adapting existing backends to new API contracts without modifying backend code.

HTTP APIs offer simpler parameter mapping (renaming headers, query strings, and path parameters) but do not support full VTL transformation. For most Lambda-backed APIs, proxy integration without transformation is sufficient and simpler to maintain.

vtl-request-template.vtl
## REST API request mapping template
## Transforms the API Gateway request into a custom format for the backend
#set($inputRoot = $input.path('$'))
{
  "action": "getUser",
  "userId": "$input.params('userId')",
  "requestedFields": [
    #foreach($field in $input.params('fields').split(","))
      "$field"#if($foreach.hasNext),#end
    #end
  ],
  "caller": {
    "sourceIp": "$context.identity.sourceIp",
    "userAgent": "$context.identity.userAgent",
    "cognitoUser": "$context.authorizer.claims.sub"
  },
  "timestamp": "$context.requestTime"
}
vtl-response-template.vtl
## REST API response mapping template
## Transforms the Lambda response into a client-friendly format
#set($inputRoot = $input.path('$'))
{
  "data": {
    "user": {
      "id": "$inputRoot.userId",
      "displayName": "$inputRoot.firstName $inputRoot.lastName",
      "email": "$inputRoot.email"
    }
  },
  "meta": {
    "requestId": "$context.requestId",
    "timestamp": "$context.requestTime"
  }
}

Authentication & Authorization

API Gateway supports multiple authentication mechanisms that can be combined for defense-in-depth. The right choice depends on your client type (browser, mobile, server), user base (internal vs external), and existing identity infrastructure.

Authentication Options

MethodBest ForAPI TypeHow It Works
IAM authorizationAWS service-to-service callsREST, HTTP, WebSocketSigV4 signed requests validated by API Gateway
Cognito authorizerUser-facing apps with Cognito User PoolsREST onlyJWT token validated by API Gateway natively
JWT authorizerAny OIDC provider (Auth0, Okta, Cognito)HTTP onlyJWT validated using JWKS endpoint
Lambda authorizer (token)Custom auth logic, API keys, legacy systemsREST, HTTP, WebSocketLambda function validates token, returns IAM policy
Lambda authorizer (request)Auth based on multiple params (headers, IP, etc.)REST, HTTPLambda receives full request context
API keysRate limiting external partnersREST onlyAPI key in x-api-key header, tied to usage plan
lambda-authorizer.ts
import {
  APIGatewayTokenAuthorizerEvent,
  APIGatewayAuthorizerResult,
} from "aws-lambda";
import jwt from "jsonwebtoken";

const SECRET = process.env.JWT_SECRET!;

export const handler = async (
  event: APIGatewayTokenAuthorizerEvent
): Promise<APIGatewayAuthorizerResult> => {
  const token = event.authorizationToken.replace("Bearer ", "");

  try {
    const decoded = jwt.verify(token, SECRET) as {
      sub: string;
      email: string;
      role: string;
    };

    return {
      principalId: decoded.sub,
      policyDocument: {
        Version: "2012-10-17",
        Statement: [
          {
            Action: "execute-api:Invoke",
            Effect: "Allow",
            Resource: event.methodArn,
          },
        ],
      },
      context: {
        userId: decoded.sub,
        email: decoded.email,
        role: decoded.role,
      },
    };
  } catch (error) {
    console.error("Authorization failed:", error);
    throw new Error("Unauthorized");
  }
};

Lambda Authorizer Caching

Lambda authorizers can cache their authorization policy for up to 3600 seconds (1 hour). This means the authorizer Lambda is not invoked for every request. The cached policy is reused for subsequent requests with the same token. This significantly reduces Lambda invocation costs and latency. Set authorizerResultTtlInSeconds based on your token expiration policy. Be careful: the cache key is the token value, so caching works best with bearer tokens and poorly with request-based authorizers that depend on multiple parameters.

Throttling & Rate Limiting

API Gateway provides built-in throttling at multiple levels to protect your backend from traffic spikes and ensure fair usage across API consumers. Throttling uses the token bucket algorithm, which allows short bursts above the steady-state rate while enforcing an average rate limit over time.

The default account-level limit is 10,000 requests per second with a burst of 5,000 requests. These limits are shared across all APIs in a region within your account. You can configure more granular limits at the stage level, route level, and per-client level (using API keys and usage plans, REST API only).

Throttling Hierarchy

LevelScopeConfiguration
Account limitAll APIs in a region10,000 RPS default (adjustable via support)
Stage limitAll routes in a stageConfigured in stage settings
Route limitIndividual routePer-method throttling (REST API)
Usage planPer API keyRate, burst, and quota (REST API only)
usage-plan.yaml
# CloudFormation - API keys with usage plans (REST API only)
Resources:
  ProductionUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      UsagePlanName: production-partners
      Description: "Rate limits for production API partners"
      Throttle:
        RateLimit: 1000    # requests per second
        BurstLimit: 2000   # burst capacity
      Quota:
        Limit: 1000000     # 1M requests
        Period: MONTH
      ApiStages:
        - ApiId: !Ref MyRestApi
          Stage: !Ref ProductionStage
          Throttle:
            "/orders/GET":
              RateLimit: 500
              BurstLimit: 1000
            "/orders/POST":
              RateLimit: 100
              BurstLimit: 200

  PartnerApiKey:
    Type: AWS::ApiGateway::ApiKey
    Properties:
      Name: partner-acme-corp
      Enabled: true

  UsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref PartnerApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ProductionUsagePlan

429 Too Many Requests

When throttling kicks in, API Gateway returns HTTP 429 (Too Many Requests) to the client. Your API clients should implement exponential backoff with jitter when they receive 429 responses. On the server side, remember that API Gateway throttling protects your API from overload but does not replace application-level rate limiting. For user-facing APIs, consider adding business-logic rate limits (e.g., "max 10 password reset attempts per hour") in your Lambda function or authorizer.

Caching Strategies

REST APIs support built-in response caching that stores backend responses at the API Gateway layer. When caching is enabled, API Gateway checks its cache before forwarding the request to your backend. If a cached response exists and has not expired, API Gateway returns it directly, and your backend is never invoked. This can dramatically reduce latency and backend load for read-heavy APIs.

Cache sizes range from 0.5 GB to 237 GB, with costs starting at $0.02/hour for the smallest tier. The cache is provisioned per stage and shared across all routes in that stage. You can override caching behavior at the method level, enabling it for GET requests while disabling it for POST requests, for example.

caching-config.yaml
Resources:
  ProductionStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: prod
      RestApiId: !Ref MyRestApi
      DeploymentId: !Ref ApiDeployment
      CacheClusterEnabled: true
      CacheClusterSize: "0.5"    # GB
      MethodSettings:
        - HttpMethod: GET
          ResourcePath: "/products"
          CachingEnabled: true
          CacheTtlInSeconds: 300     # 5 minutes
          CacheDataEncrypted: true
        - HttpMethod: GET
          ResourcePath: "/products/{productId}"
          CachingEnabled: true
          CacheTtlInSeconds: 60      # 1 minute
          CacheDataEncrypted: true
          CacheKeyParameters:
            - "method.request.path.productId"
            - "method.request.querystring.fields"
        - HttpMethod: POST
          ResourcePath: "/orders"
          CachingEnabled: false      # Never cache writes

HTTP APIs do not have built-in caching. For HTTP APIs, use CloudFront as a caching layer in front of API Gateway. CloudFront provides more flexible caching policies, edge locations for lower latency, and no per-hour cache provisioning cost. You pay only for CloudFront requests and data transfer.

Custom Domains & TLS

By default, API Gateway assigns an auto-generated URL likehttps://abc123.execute-api.us-east-1.amazonaws.com/prod. For production APIs, you should configure a custom domain name like api.example.com to provide a professional, stable, and brand-consistent endpoint.

Custom domains require an SSL/TLS certificate in AWS Certificate Manager (ACM). For edge-optimized REST APIs, the certificate must be in us-east-1. For regional REST APIs and all HTTP APIs, the certificate must be in the same region as the API.

custom-domain.yaml
Resources:
  # ACM certificate (must already be validated)
  ApiCertificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: api.example.com
      SubjectAlternativeNames:
        - "*.api.example.com"
      ValidationMethod: DNS

  # Custom domain name
  ApiDomainName:
    Type: AWS::ApiGatewayV2::DomainName
    Properties:
      DomainName: api.example.com
      DomainNameConfigurations:
        - CertificateArn: !Ref ApiCertificate
          EndpointType: REGIONAL
          SecurityPolicy: TLS_1_2

  # Map the domain to the API stage
  ApiMapping:
    Type: AWS::ApiGatewayV2::ApiMapping
    Properties:
      DomainName: api.example.com
      ApiId: !Ref MyHttpApi
      Stage: !Ref ProductionStage
      ApiMappingKey: v1   # api.example.com/v1 -> this API

  # Route 53 alias record
  ApiDnsRecord:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref HostedZoneId
      Name: api.example.com
      Type: A
      AliasTarget:
        DNSName: !GetAtt ApiDomainName.RegionalDomainName
        HostedZoneId: !GetAtt ApiDomainName.RegionalHostedZoneId

API Versioning with Custom Domains

Custom domains with base path mappings enable clean API versioning. You can mapapi.example.com/v1 to one API and api.example.com/v2 to another. When you release a new version, existing clients continue using /v1 while new clients adopt /v2. This is cleaner than version numbers in the stage name or query parameters.

Monitoring & Logging

API Gateway publishes metrics to CloudWatch automatically, including request count, latency (integration latency and total latency), 4XX and 5XX error counts, and cache hit/miss rates. For debugging, you can enable execution logging (detailed logs of each request through the API Gateway pipeline) and access logging (Apache-style access logs).

access-logging.yaml
Resources:
  ApiAccessLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /aws/apigateway/my-api-access-logs
      RetentionInDays: 30

  ProductionStage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      StageName: prod
      ApiId: !Ref MyHttpApi
      AutoDeploy: true
      AccessLogSettings:
        DestinationArn: !GetAtt ApiAccessLogGroup.Arn
        Format: >-
          {"requestId":"$context.requestId",
          "ip":"$context.identity.sourceIp",
          "method":"$context.httpMethod",
          "path":"$context.path",
          "status":"$context.status",
          "responseLength":"$context.responseLength",
          "latency":"$context.responseLatency",
          "integrationLatency":"$context.integrationLatency",
          "errorMessage":"$context.error.message",
          "authorizerError":"$context.authorizer.error",
          "userAgent":"$context.identity.userAgent"}

Key Metrics to Monitor

MetricWhat It Tells YouAlarm Threshold
5XXErrorServer-side failures> 1% of total requests
4XXErrorClient errors (may include throttling)> 10% of total requests
LatencyTotal time from request to responsep99 > SLA threshold (e.g., 3 seconds)
IntegrationLatencyTime spent in backend (Lambda, HTTP)p99 approaching Lambda timeout
CountTotal API callsUse for traffic anomaly detection
CacheHitCount / CacheMissCountCache effectivenessHit rate < 50% (cache not effective)

Execution Logging in Production

Execution logging captures the full request/response pipeline including request bodies, response bodies, and transformation results. This is invaluable for debugging but generates high log volume. Enable execution logging at the INFO level only for specific routes that need debugging, not for all routes. Use ERROR level for production to capture only failures. Be aware that execution logs may contain sensitive data (request bodies, authorization tokens), so ensure your log groups have appropriate access controls.

Best Practices & Cost Optimization

API Gateway costs are driven by request count and data transfer. Optimizing both requires understanding your API traffic patterns and choosing the right features for your use case.

Cost Optimization Strategies

Use HTTP APIs instead of REST APIs unless you need REST-specific features. HTTP APIs are 71% cheaper ($1.00 vs $3.50 per million requests) and have lower latency.

Enable caching for read-heavy APIs. A $0.02/hour cache (0.5 GB) can eliminate millions of backend invocations and their associated Lambda costs. Calculate your break-even point: if the cache saves more in Lambda costs than it costs to run, enable it.

Use CloudFront in front of HTTP APIs for edge caching. CloudFront caching is often cheaper than REST API built-in caching for high-traffic APIs.

Compress response payloads. REST APIs support response compression (gzip) when the client sends Accept-Encoding: gzip. This reduces data transfer costs and improves client-perceived latency.

Right-size Lambda functions. API Gateway latency includes integration latency (Lambda execution time). A faster Lambda function means lower API Gateway costs because the connection is held open for less time.

Security Best Practices

Always use HTTPS. API Gateway only serves HTTPS, and HTTP requests are rejected. For custom domains, use TLS 1.2 or higher.

Enable WAF on REST APIs to protect against common web exploits (SQL injection, XSS, IP-based blocking). WAF rules can be applied at the API Gateway level without modifying your backend code.

Use resource policies (REST API) to restrict API access to specific VPCs, IP ranges, or AWS accounts. This is especially important for internal APIs.

Validate request bodies (REST API) using JSON Schema request validators. This rejects malformed requests before they reach your Lambda function, saving compute costs and reducing attack surface.

Implement mutual TLS (mTLS) for high-security APIs that require client certificate authentication. Both REST and HTTP APIs support mTLS with certificates stored in ACM.

API Gateway vs Application Load Balancer

API Gateway and ALB both route HTTP requests to backends, but they serve different purposes. API Gateway is designed for API management (auth, throttling, caching, transformation, API keys) and charges per request. ALB is designed for load balancing across multiple targets and charges per hour plus per LCU. For Lambda backends, API Gateway is usually the better choice. For ECS/EC2 backends with high traffic volume, ALB may be more cost-effective. At extremely high volumes (tens of millions of requests per day), evaluate both options based on your specific feature requirements and traffic patterns.

Lambda Performance Tuning: Optimizing API Gateway Lambda BackendsCloudFront CDN: Edge Caching for API Gateway

Key Takeaways

  1. 1HTTP APIs are simpler, faster, and cheaper than REST APIs for most use cases.
  2. 2REST APIs offer more features: request validation, caching, API keys, and usage plans.
  3. 3Lambda proxy integration is the simplest pattern for connecting API Gateway to Lambda.
  4. 4Custom authorizers enable flexible authentication including JWT, OAuth, and custom logic.
  5. 5Throttling and usage plans protect backends from traffic spikes and enable monetization.
  6. 6Custom domains with ACM certificates provide professional API endpoints.

Frequently Asked Questions

Should I use REST API or HTTP API?
Use HTTP API for most new projects because it is up to 71% cheaper, has lower latency, and supports JWT authorizers natively. Use REST API only if you need features exclusive to it: request/response transformation, API caching, API keys and usage plans, or WAF integration.
How does API Gateway integrate with Lambda?
API Gateway can use Lambda proxy integration (passes the entire request to Lambda and returns the Lambda response directly) or Lambda custom integration (uses VTL templates to transform requests and responses). Proxy integration is simpler and recommended for most cases.
What is the maximum payload size for API Gateway?
Both REST and HTTP APIs support a maximum payload size of 10 MB for requests and responses. For larger payloads, use presigned S3 URLs or direct S3 uploads with API Gateway generating the URL.
How do I add caching to API Gateway?
Caching is available only on REST APIs. Enable it per stage, configure cache size (0.5 GB to 237 GB), set TTL (0-3600 seconds), and optionally require cache keys. HTTP APIs do not support built-in caching, so use CloudFront instead.
What is the cost of API Gateway?
HTTP API costs $1.00 per million requests. REST API costs $3.50 per million requests. WebSocket API costs $1.00 per million messages plus $0.25 per million connection minutes. The free tier includes 1 million REST/HTTP API calls per month for 12 months.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.