API Gateway — Cheat Sheet (DVA-C02)
Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in the guide's Changed vs older material, and in notes/current-facts.md once confirmed) · explanations and examples: notes/api-gateway-guide.md.
API types REST HTTP and WebSocket
| Topic | Current facts | Memory hook |
|---|---|---|
| REST API | Full feature set: API keys, usage plans, caching, validation, VTL, WAF, resource policies, private and edge endpoints, canary, X-Ray, streaming | REST = the full toolbox |
| HTTP API | Lower price, Regional only; IAM, JWT and Lambda authorizers; built-in CORS; auto-deploy; parameter mapping only | HTTP = the lean one |
| WebSocket API | Persistent two-way connection; messages routed by a body field; server push through @connections |
WebSocket = the open phone line |
| Control planes | REST: aws apigateway; HTTP and WebSocket: aws apigatewayv2 |
v1 vs v2 |
| REST-only features | Cache, WAF, resource policy, private endpoint, canary, X-Ray, SDK generation | Need any of them? REST |
| Not on HTTP APIs, also on WebSocket | API keys and usage plans (WebSocket: key on $connect), request validation, body transformation (VTL), MOCK |
Request/response + any of them? REST |
| HTTP-only features | JWT authorizers, automatic deployments, Cloud Map private integrations, simple (boolean) Lambda authorizer responses | |
| REST to HTTP migration | Export REST as OpenAPI 3.0, import as HTTP API; unsupported features dropped with warnings; GraphQL is AppSync, not API Gateway |
Endpoint types and custom domains
| Topic | Current facts | Memory hook |
|---|---|---|
| Edge-optimized | Documented default for REST; API Gateway-managed CloudFront POPs; header names capitalized | Edge = AWS runs the CDN |
| Regional | Same-Region clients, or your own CloudFront distribution; same custom domain in several Regions | Regional = bring your own CDN |
| Private | REST only; interface VPC endpoint for execute-api; resource policy required before deploying; TLS 1.2; dualstack only |
Private = endpoint + policy |
| Certificate for edge-optimized domain | ACM in us-east-1 | Edge certs live in Virginia |
| Certificate for Regional domain | ACM in the API's own Region | |
| DNS record | Alias to distributionDomainName (edge) or regionalDomainName (Regional) |
|
| Base path mapping | Maps api.example.com/orders to one API stage |
|
disableExecuteApiEndpoint |
Default execute-api hostname returns 403; custom domain keeps working |
|
| Private API invoke options | Private DNS URL; VPC endpoint hostname + Host or x-apigw-api-id header; alias {api-id}-{vpce-id} |
|
| VPC condition keys | aws:SourceVpc, aws:SourceVpce work on private APIs only |
|
| Endpoint type changes | Edge, Regional and private APIs can switch, except private → edge-optimized; a public custom domain can't become private | |
| Mutual TLS | Regional custom domain, TLS_1_2 policy, truststore in S3 (1,000 certificates, 1 MB); not on private APIs |
Integrations
| Topic | Current facts | Memory hook |
|---|---|---|
AWS_PROXY |
Lambda proxy: whole request in the event; function returns statusCode, headers, string body |
The function does the talking |
AWS |
Lambda custom integration or any AWS service action; mapping templates + IAM role | Straight to SQS, Kinesis, Step Functions |
HTTP_PROXY |
Pass-through to an HTTP endpoint or a private ALB or NLB | |
HTTP |
HTTP custom integration with request and response templates | |
MOCK |
No backend; prototypes, tests, CORS OPTIONS |
|
| Lambda proxy function error or malformed output | 502 Bad Gateway, body {"message": "Internal server error"} |
502 = the function broke the deal |
| Lambda refuses the invocation | 500 (missing invoke permission or Lambda throttling); fix permissions with lambda add-permission for apigateway.amazonaws.com |
No permission, 500 |
| Retries | API Gateway never retries a Lambda invocation | |
| Async Lambda from REST | Custom integration header X-Amz-Invocation-Type: 'Event' |
|
| Lambda custom integration errors | Returned as 200 unless a selectionPattern regex on errorMessage maps a status |
|
| HTTP API payload 2.0 | Adds rawPath, cookies; no multi-value fields; JSON without statusCode becomes 200 |
|
| HTTP API first-class integrations | AWS_PROXY + integrationSubtype (SQS-SendMessage, EventBridge-PutEvents, Kinesis-PutRecord, StepFunctions-StartExecution) + credentialsArn |
|
| REST private integrations | ⚠️ VPC link V2 to an ALB or an NLB | |
| HTTP API private integrations | VPC link to ALB, NLB or Cloud Map | |
| Integration types on HTTP APIs | AWS_PROXY and HTTP_PROXY only |
|
| Greedy proxy resource | {proxy+} path plus the ANY method |
Mapping templates and request validation
| Topic | Current facts | Memory hook |
|---|---|---|
| Mapping templates | VTL on REST and WebSocket non-proxy integrations, not HTTP APIs; REST picks by request Content-Type, WebSocket by a template selection expression |
VTL = JSON in, XML out |
$input.params('x') |
Looks in path, then query string, then header | |
$input.json vs $input.path |
JSON string vs object you can iterate | |
Passthrough WHEN_NO_MATCH |
Forwards unmapped content types unchanged | |
Passthrough WHEN_NO_TEMPLATES |
Recommended; rejects unmapped types once any template exists | |
Passthrough NEVER |
Rejects any content type without a matching template, even when no templates exist | |
| Rejected content type | 415 Unsupported Media Type |
|
| Validator options | Body; query strings and headers; or both | |
| Validation failure | 400 (BAD_REQUEST_BODY or BAD_REQUEST_PARAMETERS) before the backend runs |
Fail fast, pay nothing |
| Parameter validation scope | Required and non-blank only, not type or format | |
| Models | JSON Schema draft 4; body validated only for a matching Content-Type or $default |
|
| HTTP API transformations | ⚠️ Parameter mapping: request headers and query strings, path (overwrite), response headers and status; no body transformation |
Deployments stages and canary releases
| Topic | Current facts | Memory hook |
|---|---|---|
| Deployment | Immutable snapshot of the REST API; a stage points at one deployment | Snapshot + pointer |
| Redeploy needed | Methods, integrations, authorizers, models, validators, gateway responses, resource policy | Changed the API? Deploy it |
| No redeploy needed | Stage settings (cache, throttling, logging, variables, canary %), usage plans, domains, base paths | |
| Rollback | Point the stage's deploymentId at an earlier deployment |
|
| Stage variable syntax in URIs and ARNs | ${stageVariables.name} |
|
| Stage variable syntax in templates | "$stageVariables.name" (quoted) |
|
| Stage variables and secrets | Not for secrets; use authorizer context or Secrets Manager | |
| Lambda via stage variable | Same account only; one add-permission per function or alias, else 500 |
Every alias needs its own key |
| Stage variables in Lambda proxy | ⚠️ event['stageVariables'], not the context object |
|
| Canary settings | percentTraffic 0.0–100.0, stageVariableOverrides, useStageCache |
|
| Canary logs | Separate /Canary log groups and metrics |
|
| Canary promotion | Copy canary deploymentId and overrides to the stage, set % to 0; canary stays until removed |
|
| HTTP API stages | $default stage at the base URL; autoDeploy; no canary |
|
| CloudFormation deployment | AWS::ApiGateway::Deployment must DependsOn the methods |
Caching
| Topic | Current facts | Memory hook |
|---|---|---|
| Availability | REST APIs only, per stage | |
| Default scope | Enabling the stage cache caches GET methods only |
GET gets cached |
| Cache key | Resource path plus parameters marked as cache keys | Not in the key, not separated |
| Method overrides | Per method: on or off, TTL, encryption | |
| Flush everything | flush-stage-cache (or the console) |
|
| Refresh one entry | Client sends Cache-Control: max-age=0 |
|
| Invalidation permission | execute-api:InvalidateCache when Require authorization is on |
|
| Without Require authorization | Any client can bypass the cache | |
| Unauthorized invalidation strategies | FAIL_WITH_403, SUCCEED_WITH_RESPONSE_HEADER, SUCCEED_WITHOUT_RESPONSE_HEADER |
|
| Resizing | Deletes and recreates the cache, all entries lost | |
| Cost | Hourly by size, not in the Free Tier | Cache prod, not dev |
| Measuring | CacheHitCount, CacheMissCount (Average = rate) |
|
| HTTP API caching | None; put CloudFront in front |
Throttling usage plans and API keys
| Topic | Current facts | Memory hook |
|---|---|---|
| Algorithm | Token bucket: rate refills, burst is the bucket size | |
| Throttled response | 429 Too Many Requests (THROTTLED) |
429 = slow down |
| Quota exhausted | 429 (QUOTA_EXCEEDED) |
|
| Order applied | Usage plan per client or method → stage per method → account → AWS Regional | Most specific first |
| Account limit scope | Shared by all REST, HTTP and WebSocket APIs in the account and Region | One noisy API starves the rest |
| Burst increase | Not requestable; only the rate quota is adjustable | |
| Usage plan contents | API stages, optional per-method throttles, throttle (rate, burst), quota | |
| Setup order | Mark methods key-required + deploy → issue keys → define throttle and quota → attach stages and keys to the plan | |
| API key header | x-api-key (or apiKeySource = AUTHORIZER with usageIdentifierKey) |
|
| Missing or invalid key | 403 Forbidden |
|
| API keys for auth | Not authentication; identify clients for plans | Keys count, they don't guard |
| Enforcement | Best effort; use AWS Budgets and WAF for hard control | |
| HTTP API throttling | Account, stage default and per-route; no usage plans or API keys |
IAM and resource policies
| Topic | Current facts | Memory hook |
|---|---|---|
| IAM authorization | AWS_IAM on the method; SigV4 with service execute-api |
|
| Action to call an API | execute-api:Invoke |
|
| Method ARN | arn:aws:execute-api:{region}:{account-id}:{api-id}/{stage}/{verb}/{path} |
|
Method with authorization NONE |
Public; IAM policies are ignored | No AWS_IAM, no IAM check |
| Resource policies | REST only; principals, IP ranges, VPC or VPC endpoint, conditions | |
| Same-account evaluation | Allow from IAM policy or resource policy is enough, unless explicitly denied | |
| Cross-account evaluation | Caller's IAM policy and the resource policy must both allow | Two keys for two accounts |
| Resource policy change | Requires a redeploy | |
| Principal-based condition keys | aws:PrincipalArn, aws:PrincipalOrgID work only with IAM authorization |
|
Resource policy with authorization NONE |
Needs an explicit allow; a deny-only policy blocks everyone | |
| AWS WAF | REST stages only; evaluated before resource policies and authorizers; block = 403; body inspection 16 KB default, up to 64 KB | WAF goes first |
| Backend client certificate | API Gateway-generated, self-signed, 365 days; stage setting for HTTP integrations (REST, WebSocket) | |
| Cognito identity pools | Temporary AWS credentials, used with AWS_IAM methods |
Authorizers
| Topic | Current facts | Memory hook |
|---|---|---|
| Cognito user pool authorizer | REST, COGNITO_USER_POOLS; token in a header; no code |
|
| ID vs access token | No OAuth scopes on method: ID token; scopes configured: access token with a matching scope | Scopes mean access tokens |
| JWT authorizer | HTTP APIs; issuer + audience; checks iss, aud, exp, nbf, iat, scopes |
|
Lambda TOKEN authorizer |
One header (bearer token), optional regex pre-check | |
Lambda REQUEST authorizer |
Headers, query strings, stage variables, context; recommended | REQUEST sees more |
| Lambda authorizer output | principalId + IAM policy (+ context, usageIdentifierKey) |
|
| HTTP API simple responses | ⚠️ Payload 2.0 can return {"isAuthorized": true} instead of a policy |
|
| Authorizer cache TTL | Default 300 s, max 3,600 s, 0 disables | |
| Cached policy scope | Reused for other methods: cover them all, or change the cache key | |
| Deny policy | 403 Forbidden |
|
Unauthorized from the function or missing identity source |
401 |
401 = who are you? |
| Authorizer function error | 500 |
|
| Body visibility | Authorizers never receive the request body | |
| Context values (REST) | Strings, numbers, booleans only |
CORS
| Topic | Current facts | Memory hook |
|---|---|---|
| Preflight request | Browser sends OPTIONS with Origin and Access-Control-Request-Method |
|
| Preflight response | ⚠️ Returns Access-Control-Allow-Origin, -Methods, -Headers |
Allow headers ride on the response |
| Console Enable CORS (REST) | MOCK OPTIONS method + Access-Control-Allow-Origin on the 200 response; redeploy |
|
| Proxy integrations | Backend must return Access-Control-Allow-Origin itself |
Proxy = you send the headers |
| API Gateway error responses | Add CORS headers to DEFAULT_4XX and DEFAULT_5XX gateway responses |
|
| Enable CORS on child resources | Not recursive; enable per resource | |
| HTTP API CORS | corsConfiguration; API Gateway answers preflights and ignores backend CORS headers |
|
HTTP API with authorized $default |
Add an OPTIONS /{proxy+} route with an integration and no authorizer |
|
| Credentialed requests | Return the exact origin, not * |
|
| Scope of CORS | Browser rule only; not access control |
Errors timeouts and limits
| Topic | Current facts | Memory hook |
|---|---|---|
| 400 | Request validation failed | |
| 401 | Authorizer rejected the caller or an identity source is missing | |
| 403 | Denied by policy or authorizer, bad or missing API key, WAF block, unsigned IAM call, unknown path or stage | 403 has many parents |
| 403 Missing Authentication Token | Usually a wrong path, method or stage | |
| 413 | Request body over 10 MB | |
| 415 | Content type rejected by passthrough behavior | |
| 429 | Throttled or quota exceeded | |
| 500 | Configuration error, authorizer failure, missing Lambda invoke permission, Lambda throttling | |
| 502 | Lambda proxy function error or malformed output | |
| 503 | No REST gateway response defaults to it: usually the backend's own 503 | |
| 504 | Integration timeout or failure | 504 = waited too long |
| Long-running work | Async job + polling, WebSocket push, response streaming, or a longer Regional/private timeout | |
| Large uploads | S3 presigned URL instead of the API | |
| Lambda payload behind API Gateway | Lambda caps synchronous payloads at 6 MB |
Logging metrics and tracing
| Topic | Current facts | Memory hook |
|---|---|---|
| Execution log levels | ⚠️ OFF, ERROR, INFO, plus a separate data-tracing switch |
No DEBUG level |
| Execution log group | API-Gateway-Execution-Logs_{rest-api-id}/{stage_name} |
|
| Data tracing | Logs full payloads; avoid in production | |
| Access logs | Your format from $context variables; CLF, JSON, XML or CSV; REST also to Firehose |
|
| Logging prerequisite (REST, WebSocket) | Account cloudwatchRoleArn per Region, role with AmazonAPIGatewayPushToCloudWatchLogs |
No role, no logs |
| HTTP API access logs | Log delivery: the user needs logs:CreateLogDelivery and related permissions; no account role |
|
Latency vs IntegrationLatency |
Total time in API Gateway vs time waiting for the backend | Gap = API Gateway's own work |
| REST error metrics | 4XXError, 5XXError |
|
| HTTP API error metrics | 4xx, 5xx |
|
| Detailed metrics | Per-method or per-route dimensions; extra cost | |
| X-Ray active tracing | REST stage setting (tracingEnabled), all endpoint types; not on HTTP APIs |
|
| HTTP API observability | Access logs and metrics only |
WebSocket APIs
| Topic | Current facts | Memory hook |
|---|---|---|
$connect |
Runs during the handshake; the only place for authorization | Check IDs at the door |
$disconnect |
Runs after close; best effort | |
$default |
Messages matching no route, or non-JSON | |
| Route selection expression | Usually $request.body.action |
|
| Callback endpoint | ⚠️ https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connectionId} |
Push over HTTPS, not wss |
| Callback operations | POST send, GET status, DELETE disconnect |
|
| Callback permission | execute-api:ManageConnections, SigV4-signed |
|
| Gone connection | GoneException: delete the stored connection ID |
|
| SDK client | boto3 apigatewaymanagementapi, post_to_connection |
|
| Close code 1009 | Message or frame over the size quota |
OpenAPI and SDKs
| Topic | Current facts | Memory hook |
|---|---|---|
| REST import formats | OpenAPI 2.0 or 3.0, JSON or YAML | |
| HTTP API import format | OpenAPI 3.0 only | |
| Create from a file | import-rest-api (--parameters endpointConfigurationTypes=REGIONAL) |
|
| Update from a file | put-rest-api --mode merge (default) or --mode overwrite |
|
| merge | Keeps what the file doesn't define | Merge adds |
| overwrite | Replaces the definition; unspecified settings reset | Overwrite wipes |
| After import | Still needs a deployment | |
| Export | get-export --export-type oas30 or swagger, extensions='integrations' or 'apigateway', YAML via --accepts application/yaml |
Export with extensions |
| Models on export | Must be application/json, or the export fails |
|
| SDK generation | REST only; Java, JavaScript, Android, iOS (Objective-C, Swift), Ruby; get-sdk |
|
| HTTP API export | export-api --specification OAS30 --output-type YAML |
Numbers to know
| Item | Value | Note |
|---|---|---|
| Account throttle (default) | 10,000 rps, burst 5,000 per account per Region ⚠️ | 2,500 rps / 1,250 burst in 13 newer Regions |
| REST integration timeout | 50 ms–29 s default ⚠️ | Regional and private can go higher via Service Quotas L-E5AE38E3 |
| Edge-optimized integration timeout | 29 s, not raisable | |
| HTTP API integration timeout | 30 s, fixed | |
| WebSocket integration timeout | 29 s | |
| Payload size (REST and HTTP) | 10 MB, not adjustable | |
| REST response streaming duration | Up to 15 min ⚠️ | Over 10 MB allowed; 2 MB/s after the first 10 MB |
| Streaming idle timeout | 5 min Regional/private, 30 s edge ⚠️ | |
| Cache TTL | 300 s default, 0–3,600 s | 0 disables caching |
| Cache sizes | 0.5, 1.6, 6.1, 13.5, 28.4, 58.2, 118, 237 GB | |
| Largest cacheable response | 1,048,576 bytes (1 MB) | |
| Cache provisioning time | Up to about 4 minutes | |
| Lambda authorizer cache TTL | 300 s default, 3,600 s max | |
| Authorizers per API | 10 | Raise via AWS Support |
| Cognito user pools per authorizer | 1,000 | Fixed |
| Stages per API | 10 | Adjustable |
| Resources per REST API | 300 | Adjustable; HTTP API routes also 300 |
| Stage variables per stage | 100 | Key 64 chars, value 512 chars |
| Stage name length | 128 characters | Letters, digits, -, _ |
| API key value length | 20–128 characters | |
| API keys per account per Region | 10,000 | Not adjustable |
| Usage plans per account per Region | 300 | Adjustable |
| Usage plans per API key | 10 | One plan per key per stage |
| Quota periods | DAY, WEEK, MONTH |
|
| Canary traffic | 0.0–100.0 % | |
CreateDeployment rate |
1 request every 5 s per account | Fixed |
| Mapping template size | 300 KB | #foreach max 1,000 iterations |
| Model size per API | 400 KB | |
| Resource policy length | 8,192 characters | Adjustable |
| Method ARN length | 1,600 bytes | Longer → 414 |
| REST header size | 20,480 bytes (private APIs 8,000) | |
| HTTP API request line + headers | 10,240 bytes | |
| WebSocket new connections | 500 per second per account per Region | Adjustable |
| WebSocket connection lifetime | 2 hours | Idle timeout 10 minutes |
| WebSocket message size | 128 KB | Frames of 32 KB |
| Public custom domains | 120 per account per Region | Adjustable |
| JWT authorizer limits | 50 audiences, 10 scopes per route | Keys cached up to 2 h |
| HTTP API Lambda authorizer timeout | 10,000 ms | |
| OpenAPI import file | 6 MB | |
| Access log format size | 3 KB |
Decision rules
- If you see API keys, usage plans, caching, request validation, WAF or a private endpoint, think REST API.
- If you see the lowest cost for a Lambda backend with JWT authentication and no REST feature that HTTP APIs lack, think HTTP API with a JWT authorizer.
- If you see server push or chat over a persistent connection, think WebSocket API.
- If you see an edge-optimized custom domain, think ACM certificate in us-east-1.
- If you see a Regional custom domain, think ACM certificate in the API's Region.
- If you see "reachable only from the VPC", think private REST API + interface VPC endpoint +
aws:SourceVpceresource policy. - If you see global users plus your own caching or edge security, think Regional API behind your own CloudFront.
- If you see a Lambda proxy integration returning 502, think function error or wrong response shape:
statusCodeand a stringbody. - If you see 500 after creating a Lambda integration with the CLI or CloudFormation, think missing
lambda:InvokeFunctionpermission forapigateway.amazonaws.com. - If you see API Gateway writing to SQS or Kinesis without business logic, think AWS service integration with an IAM role.
- If you see "invoke the Lambda function asynchronously from the API", think
X-Amz-Invocation-Type: 'Event'on a custom integration. - If you see a SOAP/XML backend and JSON clients with no extra compute, think REST custom integration with VTL templates.
- If you see "reject malformed requests before the backend", think request validator and model.
- If you see 415, think passthrough behavior and missing mapping template.
- If you see API changes that don't take effect, think redeploy to the stage.
- If you see different Lambda versions per stage, think stage variable pointing at a Lambda alias.
- If you see a new stage variable alias returning 500, think missing invoke permission for that alias.
- If you see "test a new API version on a small share of traffic", think canary release on the stage, then promote.
- If you see repeated GETs overloading the backend, think stage cache with a TTL.
- If you see cached responses mixed between users or filters, think missing cache key parameter.
- If you see clients bypassing the cache, think Require authorization plus
execute-api:InvalidateCache. - If you see per-customer quotas or tiers, think usage plans with API keys.
- If you see 429s across unrelated APIs in one account, think shared account throttle: per-stage limits and a quota increase.
- If you see callers that hold AWS credentials, think
AWS_IAMauthorization and SigV4. - If you see cross-account IAM callers denied despite the resource policy, think the caller's identity policy must also allow.
- If you see third-party tokens or rules over several request values, think Lambda
REQUESTauthorizer. - If you see a CORS error with a proxy integration, think the backend must return
Access-Control-Allow-Origin. - If you see 504 near 29 seconds, think integration timeout: go asynchronous, stream, or raise it on a Regional or private API.
- If you see 413 on uploads, think S3 presigned URL.
- If you see high
Latencybut lowIntegrationLatency, think authorizer, mapping or WAF time inside API Gateway.
Memory hooks
- REST is the full toolbox, HTTP is the lean one, WebSocket is the open phone line.
- Need a REST feature that HTTP APIs lack? Cost no longer decides.
- Edge certificates live in Virginia; Regional certificates live with the API.
- Private API = VPC endpoint + resource policy, and the policy comes first.
- Proxy means the function talks:
statusCodeplus a stringbody. - 502 = the function broke the deal (error or bad shape); 504 = waited too long; 500 = check permissions first.
- No permission, 500: every alias needs its own
add-permission. - Changed the API? Deploy it. Changed a stage setting? It's live.
- A deployment is a snapshot; a stage is a pointer.
- Canary splits the stage; alias weights split the function.
- GET gets cached; anything not in the cache key gets mixed.
- Cache prod, not dev.
- Keys count, they don't guard: API keys are for plans, not authentication.
- 429 = slow down, retry with backoff.
- Most specific throttle first: usage plan, stage method, account, Region.
- Two keys for two accounts: cross-account needs both policies.
- No
AWS_IAM, no IAM check. - WAF goes first, before policies and authorizers.
- Scopes on the method mean access tokens.
- 401 = who are you? 403 = you may not.
- Authorizers never see the body.
- Proxy CORS: you send the headers; error CORS: gateway responses.
- Check IDs at the door: WebSocket auth happens on
$connect. - Push over HTTPS, not wss:
@connectionsis a signed call. - Merge adds, overwrite wipes; an import still needs a deployment.