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.
Prerequisites
- Basic understanding of REST APIs and HTTP methods
- Familiarity with AWS Lambda
- AWS account with API Gateway permissions
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.
| Feature | REST API | HTTP API | WebSocket API |
|---|---|---|---|
| Cost (per million requests) | $3.50 | $1.00 | $1.00 + $0.25/million connection min |
| Latency | Higher (~30ms overhead) | Lower (~10ms overhead) | Low (persistent connection) |
| Lambda integration | Proxy & custom | Proxy only (v2 payload) | Proxy |
| Request transformation | VTL mapping templates | Parameter mapping only | Route selection expression |
| Response transformation | VTL mapping templates | No | No |
| Caching | Built-in (0.5 GB – 237 GB) | No (use CloudFront) | No |
| API keys & usage plans | Yes | No | No |
| Request validation | Yes (JSON Schema) | No | No |
| WAF integration | Yes | No | Yes |
| Resource policies | Yes | No | No |
| Private endpoints | Yes (VPC endpoint) | Yes (VPC link) | No |
| Edge-optimized endpoint | Yes | No | No |
| Mutual TLS | Yes | Yes | No |
| Auth options | IAM, Cognito, Lambda authorizer | IAM, JWT (native), Lambda authorizer | IAM, 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)
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)
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.
## 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"
}## 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
| Method | Best For | API Type | How It Works |
|---|---|---|---|
| IAM authorization | AWS service-to-service calls | REST, HTTP, WebSocket | SigV4 signed requests validated by API Gateway |
| Cognito authorizer | User-facing apps with Cognito User Pools | REST only | JWT token validated by API Gateway natively |
| JWT authorizer | Any OIDC provider (Auth0, Okta, Cognito) | HTTP only | JWT validated using JWKS endpoint |
| Lambda authorizer (token) | Custom auth logic, API keys, legacy systems | REST, HTTP, WebSocket | Lambda function validates token, returns IAM policy |
| Lambda authorizer (request) | Auth based on multiple params (headers, IP, etc.) | REST, HTTP | Lambda receives full request context |
| API keys | Rate limiting external partners | REST only | API key in x-api-key header, tied to usage plan |
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
| Level | Scope | Configuration |
|---|---|---|
| Account limit | All APIs in a region | 10,000 RPS default (adjustable via support) |
| Stage limit | All routes in a stage | Configured in stage settings |
| Route limit | Individual route | Per-method throttling (REST API) |
| Usage plan | Per API key | Rate, burst, and quota (REST API only) |
# 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 ProductionUsagePlan429 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.
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 writesHTTP 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.
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.RegionalHostedZoneIdAPI 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).
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
| Metric | What It Tells You | Alarm Threshold |
|---|---|---|
5XXError | Server-side failures | > 1% of total requests |
4XXError | Client errors (may include throttling) | > 10% of total requests |
Latency | Total time from request to response | p99 > SLA threshold (e.g., 3 seconds) |
IntegrationLatency | Time spent in backend (Lambda, HTTP) | p99 approaching Lambda timeout |
Count | Total API calls | Use for traffic anomaly detection |
CacheHitCount / CacheMissCount | Cache effectiveness | Hit 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.
Key Takeaways
- 1HTTP APIs are simpler, faster, and cheaper than REST APIs for most use cases.
- 2REST APIs offer more features: request validation, caching, API keys, and usage plans.
- 3Lambda proxy integration is the simplest pattern for connecting API Gateway to Lambda.
- 4Custom authorizers enable flexible authentication including JWT, OAuth, and custom logic.
- 5Throttling and usage plans protect backends from traffic spikes and enable monetization.
- 6Custom domains with ACM certificates provide professional API endpoints.
Frequently Asked Questions
Should I use REST API or HTTP API?
How does API Gateway integrate with Lambda?
What is the maximum payload size for API Gateway?
How do I add caching to API Gateway?
What is the cost of API Gateway?
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.