API Gateway — DVA-C02 Mastery Guide
- Sources: course chapter 16 (API Gateway). The course serves as the syllabus only; all wording here is original. Neighbouring topics are cross-referenced to the module that owns them (Lambda aliases, Cognito user pools, SigV4 signing, CloudFront, X-Ray instrumentation).
- Written 2026-10-12; values checked against AWS docs on 2026-10-12. The marker
⚠️ Changed: <current>. Older material may say <old>.flags a value or behaviour that differs from the course or older practice tests: learn the current value.⚠️ unverifiedmarks something that could not be confirmed in current AWS documentation: do not rely on it. Every change is listed in Changed vs older material; this module's rows innotes/current-facts.mdare pending your confirmation.- Tier 1 (highest exam weight), bank target 40 questions. Domains: Development with AWS Services (primary), Deployment, Security.
- How to use it:
/explain <topic>teaches from these sections (concept → exam facts → developer angle → traps → 3 check questions);/quiz api-gatewayasks bank questions whosesource_notelinks point at the## N.headings below;/drill api-gatewayfires short recall prompts fromnotes/api-gateway-cheat-sheet.md. Suggested loop: read a section and answer its practice questions before opening the answers →/explainwhat is still unclear →/quiz api-gateway→/drill api-gateway.
How API Gateway appears on DVA-C02
API Gateway is the front door of nearly every serverless scenario on the exam. Questions seldom ask what it is; they ask which API type, integration, setting or authorizer meets a stated constraint, and how to read the status code a client receives.
- Development: REST vs HTTP vs WebSocket; Lambda proxy vs custom integrations; VTL mapping templates for XML or legacy backends; direct AWS service integrations; the event and response shapes your Lambda code must produce.
- Deployment: deployments and stages, stage variables that point at Lambda aliases, canary releases, OpenAPI import and export, SDK generation.
- Security: IAM (SigV4) vs Cognito vs Lambda vs JWT authorizers, resource policies for cross-account, IP and VPC control, API keys (metering, not authentication), private APIs, AWS WAF.
- Troubleshooting flavour: 403 variants, 429 throttling, 502 function errors or malformed proxy output, 504 timeouts, CORS failures, cache hit ratio,
LatencyvsIntegrationLatency. In this module's bank such questions are framed as development or security tasks; on the real exam they also feed the Troubleshooting and Optimization domain.
Typical question shapes: a scenario with one deciding constraint ("LEAST operational overhead", "without changing the Lambda code", "per-customer limits"); an error code or metric to diagnose; "Which TWO…" with five options; the exact setting, header or CLI command that does the job.
Domain map: Development → §1, §3, §4, §6, §11, §13 · Deployment → §5, §12, §14 · Security → §2 (private APIs), §7 (API keys and throttling), §8, §9, §10.
Out of scope here: network design (VPC link subnets, endpoint DNS architecture) beyond the settings a developer sets; Lambda internals (see notes/lambda-guide.md); Cognito user pool configuration (see notes/advanced-identity-guide.md); SigV4 mechanics and retries with backoff (see notes/cli-sdk-policies-guide.md); CloudFront behaviours (see notes/cloudfront-guide.md).
| # | Topic | Why it is on the exam |
|---|---|---|
| 1 | API types REST HTTP and WebSocket | Picking the product from a feature list, a cost target or a push requirement |
| 2 | Endpoint types and custom domain names | Edge vs Regional vs private; certificate Region; VPC-only access |
| 3 | Integration types proxy and non proxy | The Lambda proxy contract, service integrations, async Lambda, invoke permissions |
| 4 | Mapping templates models and request validation | VTL transformations, 415 passthrough rejections, 400 validation before the backend |
| 5 | Deployments stages stage variables and canary releases | Redeploy rules, stage variables plus Lambda aliases, canary promotion |
| 6 | Caching | TTLs, cache keys, invalidation permissions, REST-only |
| 7 | Throttling usage plans and API keys | Token bucket levels, 429s, per-client plans, API keys are not authentication |
| 8 | IAM authorization and resource policies | SigV4, execute-api:Invoke ARNs, cross-account evaluation, WAF |
| 9 | Cognito JWT and Lambda authorizers | Choosing the authorizer, cached decisions, 401 vs 403 |
| 10 | CORS | Preflight, proxy integrations returning headers, gateway responses |
| 11 | Error codes timeouts and payload limits | Each code to its cause; the 29 s and 10 MB limits and their exceptions |
| 12 | Logging metrics and tracing | Execution vs access logs, the CloudWatch role, metrics, X-Ray |
| 13 | WebSocket APIs | Routes, connection IDs, @connections callbacks |
| 14 | OpenAPI import export and SDK generation | API as code, merge vs overwrite, export extensions, client SDKs |
1. API types REST HTTP and WebSocket
Concept
- API Gateway sells three API products. REST APIs use the original control plane (
aws apigateway, sometimes called v1) and carry the full feature set. HTTP APIs and WebSocket APIs share the newer control plane (aws apigatewayv2). - REST API: request/response API with every management feature: API keys and usage plans, per-client throttling, caching, request validation, VTL body transformations, mock integrations, AWS WAF, resource policies, private and edge-optimized endpoints, canary releases, X-Ray tracing, custom gateway responses, SDK generation, a developer portal and response streaming.
- HTTP API: a deliberately smaller product offered at a lower price. It keeps what most Lambda and HTTP backends need: Lambda proxy and HTTP proxy integrations, private integrations (ALB, NLB, Cloud Map), first-class integrations with selected AWS service actions, JWT authorizers (OIDC and OAuth 2.0), Lambda authorizers, IAM authorization, built-in CORS, parameter mapping, automatic deployments and a
$defaultstage served from the base URL. - WebSocket API: holds a persistent connection per client so the backend can push messages at any time; incoming messages are routed by a value in their JSON body (§13). It shares some REST features that HTTP APIs lack: API keys and usage plans (the
$connectroute can require a key), request validation, VTL mapping templates and MOCK integrations. - Working rule: read the requirement list first. The server must push data over a long-lived connection → WebSocket. Otherwise, any feature in it that HTTP APIs lack (table below) → REST. None, and cost or latency matters → HTTP.
Exam facts
| Feature | REST API | HTTP API |
|---|---|---|
| Endpoint types | Edge-optimized, Regional, private | Regional only |
| API keys, usage plans, per-client throttling | Yes | No |
| Caching | Yes | No (put CloudFront in front) |
| Request validation, body transformation (VTL) | Yes | No (parameter mapping only) |
| Mock integrations, custom gateway responses, console test invoke | Yes | No |
| AWS WAF, resource policies, private endpoint | Yes | No |
| Canary release deployments | Yes | No |
| Automatic deployments | No | Yes |
| Authorizers | IAM, Cognito user pool, Lambda (TOKEN or REQUEST) |
IAM, JWT (Cognito or any OIDC issuer), Lambda (REQUEST) |
| X-Ray tracing, execution logs, access logs to Firehose | Yes | No (access logs to CloudWatch Logs) |
| Private integrations | ALB or NLB (VPC link V2) | ALB, NLB or Cloud Map |
| Client certificate for backend authentication | Yes | No |
| Response streaming | Yes | No |
- Integration timeout (§11): REST 50 ms to 29 s by default, raisable only for Regional and private REST APIs; HTTP APIs 30 s, fixed; WebSocket 29 s. Payload: 10 MB for REST and HTTP APIs; WebSocket messages 128 KB in frames of up to 32 KB.
- The account-level throttle (10,000 requests per second with a 5,000 burst by default) is one pool shared by REST, HTTP and WebSocket APIs and WebSocket callbacks in the account and Region (§7).
- HTTP API Lambda integrations choose
payloadFormatVersion1.0(same shape as REST) or2.0(leaner event, inferred responses) (§3). - Moving from REST to HTTP: export the REST API as OpenAPI 3.0 and import it as an HTTP API; unsupported features are reported as import info or warnings and dropped (§14). GraphQL APIs and subscriptions are AWS AppSync, not API Gateway (
notes/other-services-guide.md).
Developer angle
# REST API (v1 control plane): set the endpoint type explicitly
aws apigateway create-rest-api --name orders-api --endpoint-configuration types=REGIONAL
# HTTP API quick create: API + Lambda integration + catch-all route + auto-deploying $default stage
aws apigatewayv2 create-api --name orders-http --protocol-type HTTP \
--target arn:aws:lambda:eu-west-1:111122223333:function:orders-api
# WebSocket API: the route selection expression is an API-level setting
aws apigatewayv2 create-api --name chat-ws --protocol-type WEBSOCKET --route-selection-expression '$request.body.action'
- CloudFormation:
AWS::ApiGateway::RestApiwithResource,Method,DeploymentandStageresources for REST;AWS::ApiGatewayV2::ApiwithProtocolType: HTTPorWEBSOCKETfor v2 (SAM shortcuts:notes/sam-guide.md). boto3 clients:apigateway(REST),apigatewayv2(HTTP and WebSocket),apigatewaymanagementapi(WebSocket@connectionscalls).
Traps
- Trap: "Use an HTTP API because it is cheaper" when the stem also needs API keys, usage plans, caching, request validation or WAF. → Reality: HTTP APIs have none of them; a hard feature requirement beats cost.
- Trap: "An HTTP API can be private." → Reality: an HTTP API can reach private backends through a VPC link but cannot have a private endpoint; only REST APIs can be private.
- Trap: "Attach a JWT authorizer to a REST API." → Reality: JWT authorizers belong to HTTP APIs; a REST API uses a Cognito user pool authorizer or a Lambda authorizer that validates the token.
- Trap: "Use a WebSocket API for ordinary CRUD calls." → Reality: WebSocket adds connection bookkeeping; choose it when the server must push.
- Trap: "HTTP APIs support edge-optimized endpoints." → Reality: Regional only; add your own CloudFront distribution for edge delivery.
Practice questions
-
A team is building a public API for a mobile app. Every route calls one Lambda function, users present JWTs issued by a third-party OpenID Connect provider, and the API must cost as little as possible. No caching or per-customer quotas are needed. Which option meets these requirements?
- A. A REST API with a Lambda authorizer that validates the JWT on each request
- B. A REST API with an Amazon Cognito user pool authorizer federated to the provider
- C. An HTTP API with a JWT authorizer for the provider's issuer and audience
- D. A WebSocket API with a Lambda authorizer on the
$connectroute
-
A company must expose an API that enforces request quotas per customer, blocks SQL injection attempts with managed rules, and caches responses for 60 seconds. Which TWO statements about this design are true? (Select TWO.)
- A. An HTTP API with route-level throttling enforces the per-customer quotas.
- B. The API has to be a REST API, because usage plans, AWS WAF and caching are REST features.
- C. An HTTP API can associate an AWS WAF web ACL with its
$defaultstage. - D. A 60-second cache can be enabled on an HTTP API stage.
- E. Customers are identified by API keys associated with a usage plan.
Answers
- C. HTTP APIs validate OIDC/OAuth 2.0 JWTs natively and are priced below REST APIs. A works but costs more (REST pricing plus authorizer invocations); B adds a Cognito user pool the requirement does not need; D is built for persistent two-way connections.
- B, E. Usage plans with API keys, WAF and caching are REST features that HTTP APIs lack, and a usage plan recognises customers by their API key. A fails because HTTP API throttling is per stage or route, not per client; C and D fail because HTTP APIs support neither WAF nor caching.
2. Endpoint types and custom domain names
Concept
- The endpoint type decides where clients connect. Edge-optimized: requests enter at the nearest CloudFront point of presence that API Gateway runs on your behalf and are carried to the API's home Region; AWS documents it as the default endpoint type for REST APIs. Regional: clients connect to the Region directly; best for callers in the same Region, or when you want your own CloudFront distribution with your own cache and edge settings. Private: reachable only through an interface VPC endpoint for
execute-apiinside your VPC (on-premises networks reach it through Direct Connect into that VPC). - A custom domain name (
api.example.com) replaces the generated host inhttps://{api-id}.execute-api.{region}.amazonaws.com/{stage}. You attach an ACM certificate, map API stages to base paths (API mappings) and point DNS at the target API Gateway gives you. Base paths let one hostname front several APIs or versions (/orders,/payments,/v2): the usual single entry point for a set of microservices. - An edge-optimized custom domain applies across all Regions; a Regional custom domain is specific to one Region, so the same name can be created in several Regions and steered with Route 53 latency or failover routing (see
notes/route53-guide.md).
Exam facts
- Certificate Region: edge-optimized custom domain → ACM certificate in us-east-1; Regional custom domain → ACM certificate in the same Region as the API. ACM validity and renewal: see
notes/ec2-mastery-guide.md§9 andnotes/current-facts.md. - DNS target: an alias (or CNAME) record to the domain's
distributionDomainName(edge-optimized) orregionalDomainName(Regional). - Endpoint types by product: REST = edge-optimized, Regional or private; HTTP = Regional. A Regional custom domain can map both REST and HTTP APIs.
- Switching: an API moves between edge-optimized, Regional and private, except that a private API can't become edge-optimized; a custom domain moves between edge-optimized and Regional, but a public custom domain can't become a private one.
- Private APIs: REST only; TLS 1.2 only; IP address type
dualstackonly. A resource policy must be attached before the API can be deployed; restrict callers withaws:SourceVpceoraws:SourceVpc(keys that work only on private APIs). - Invoking a private API: with private DNS on the endpoint,
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}; without it, the endpoint-specific hostname plus aHostorx-apigw-api-idheader; or, after associating the endpoint with the API, the aliashttps://{api-id}-{vpce-id}.execute-api.{region}.amazonaws.com/{stage}. - Turning on private DNS for the
execute-apiendpoint makes the default endpoints of public APIs unreachable from that VPC (their custom domains still work). Edge-optimized APIs capitalize header names (for exampleCookie); Regional and private APIs pass them through unchanged. disableExecuteApiEndpoint = true: the defaultexecute-apihostname answers403 Forbidden, so clients must use the custom domain; it takes effect at the next stage update or redeploy.- Mutual TLS (REST and HTTP APIs): a Regional custom domain with the
TLS_1_2security policy and a truststore in S3 (up to 1,000 certificates, 1 MB); not available for private APIs; an untrusted or expired client certificate fails with403. - Wildcard custom domains (
*.example.com) need an ACM-issued certificate. Quotas per account per Region: 120 public custom domains (adjustable); 600 Regional, 600 private and 120 edge-optimized REST APIs.
Developer angle
# Regional custom domain with a certificate from the API's own Region
aws apigateway create-domain-name --domain-name api.example.com \
--regional-certificate-arn arn:aws:acm:eu-west-1:111122223333:certificate/0f1e2d3c-4b5a-6978-8a9b-0c1d2e3f4a5b \
--endpoint-configuration types=REGIONAL --security-policy TLS_1_2
# Map the prod stage to https://api.example.com/orders
aws apigateway create-base-path-mapping --domain-name api.example.com \
--rest-api-id a1b2c3d4e5 --stage prod --base-path orders
# Call a private API from inside the VPC when private DNS is off
curl -s https://vpce-0a1b2c3d4e5f60718-abcdefgh.execute-api.eu-west-1.vpce.amazonaws.com/prod/orders \
-H 'x-apigw-api-id: a1b2c3d4e5'
Resource policy for a private API: allow invocation, then deny anything that did not arrive through one VPC endpoint.
{"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "execute-api:/*"},
{"Effect": "Deny", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "execute-api:/*",
"Condition": {"StringNotEquals": {"aws:SourceVpce": "vpce-0a1b2c3d4e5f60718"}}}]}
Traps
- Trap: "Request the edge-optimized domain's certificate in the API's Region." → Reality: edge-optimized needs us-east-1; only Regional custom domains use the API's Region.
- Trap: "Global users, so a Regional API is wrong." → Reality: when the team must control caching or edge security itself, a Regional API behind its own CloudFront distribution is the documented pattern.
- Trap: "Restrict a Regional API to one VPC with
aws:SourceVpce." → Reality: the VPC condition keys only work on private APIs; make the API private. - Trap: "Deploy the private API first and add the resource policy later." → Reality: a private API without a resource policy cannot be deployed.
- Trap: "Disabling the default endpoint takes the API offline." → Reality: only the
execute-apihostname returns 403; the custom domain keeps serving traffic.
Practice questions
-
A Regional REST API in
ap-southeast-2serves clients in Australia only, and its custom domain must stay Regional. Creating the domain fails with the team's ACM certificate, which was issued inus-east-1. What should the developer do?- A. Request or import an ACM certificate for the domain in
ap-southeast-2. - B. Create the custom domain as edge-optimized so the
us-east-1certificate is accepted. - C. Point a CNAME record for the domain at the API's default
execute-apihostname. - D. Attach the certificate to the API's
prodstage in the stage settings.
- A. Request or import an ACM certificate for the domain in
-
An internal REST API must be callable only by applications in one VPC, and the traffic must not traverse the internet. Which TWO steps are required? (Select TWO.)
- A. Create the API with the private endpoint type and an interface VPC endpoint for
execute-apiin the VPC. - B. Create an HTTP API and attach a VPC link to the application subnets.
- C. Keep a Regional endpoint and allow the VPC's private CIDR range with
aws:SourceIpin a resource policy. - D. Attach a resource policy that allows
execute-api:Invokeonly whenaws:SourceVpcematches the VPC endpoint. - E. Set
disableExecuteApiEndpointto keep internet clients out.
- A. Create the API with the private endpoint type and an interface VPC endpoint for
Answers
- A. A Regional custom domain takes an ACM certificate from the API's own Region. B would accept the
us-east-1certificate but breaks the requirement to keep the domain Regional; C bypasses the custom domain, so clients see a certificate for the wrong hostname; D fails because certificates belong to the custom domain name, not to a stage. - A, D. A private endpoint plus a resource policy scoped to the VPC endpoint keeps traffic inside AWS. B gives an API private backends, not a private front door; C still exposes a public endpoint and private IPs never appear as the source; E only forces clients onto a custom domain.
3. Integration types proxy and non proxy
Concept
- A REST method is processed in four parts: method request (authorization, API key, parameters, validation) → integration request (what API Gateway sends to the backend) → integration response (how the backend reply is mapped) → method response (status codes, headers and models clients see). Proxy integrations skip the integration request and response mapping and pass everything through.
- Integration
typevalues:
| Type | Backend | What you configure | Typical use |
|---|---|---|---|
AWS_PROXY |
Lambda (Lambda proxy) | Nothing to map; the function parses the request and returns status, headers and body | Default choice for Lambda backends |
AWS |
Lambda (custom) or any AWS service action | Integration request and response, mapping templates, an IAM role | Call SQS, Kinesis, Step Functions or DynamoDB directly; reshape Lambda input and output |
HTTP_PROXY |
HTTP endpoint | The URL, optionally header or parameter mappings | Pass-through to an existing HTTP service or a private ALB or NLB |
HTTP |
HTTP endpoint (custom) | Integration request and response with templates | Translate between client and legacy backend formats |
MOCK |
None | A request template that picks the status code, plus integration responses | Prototypes, tests, CORS preflight |
- Private integrations (
connectionType: VPC_LINK) reach load balancers inside a VPC. ⚠️ Changed: REST APIs connect through a VPC link V2 to an Application Load Balancer or a Network Load Balancer (anHTTP_PROXYintegration; existing VPC link V1 integrations, NLB only, can be switched to V2). Older material may say REST private integrations need a Network Load Balancer. HTTP APIs reach ALB, NLB or AWS Cloud Map. Network setup: seenotes/vpc-guide.md. - AWS service integrations let API Gateway call a service API itself, with no function in between. REST: type
AWS, a service URI, an IAM role as the integration credentials and a mapping template that builds the service request. HTTP APIs: first-class integrations (AWS_PROXYplus anintegrationSubtypesuch asSQS-SendMessage,EventBridge-PutEvents,Kinesis-PutRecordorStepFunctions-StartExecution) configured with parameter mapping andcredentialsArn. - Invocation mode: Lambda proxy integrations invoke the function synchronously. A Lambda custom integration can invoke it asynchronously by sending
X-Amz-Invocation-Type: Eventto Lambda, either as a static'Event'value or mapped from a client header (method.request.header.InvocationType, so callers pickEventorRequestResponse); the client then gets the integration response at once while the function runs.
Exam facts
- Lambda proxy request (REST, format 1.0):
resource,path,httpMethod,headers,multiValueHeaders,queryStringParameters,multiValueQueryStringParameters,pathParameters,stageVariables,requestContext(stage, request ID, identity, authorizer data),body(a string) andisBase64Encoded. - Lambda proxy response:
statusCode,headers,multiValueHeaders,body(a string) andisBase64Encoded. Output in any other shape, or a function that runs but fails, →502 Bad Gatewaywith the body{"message": "Internal server error"}. - If Lambda refuses the invocation (no invoke permission, Lambda throttling), API Gateway returns
500. API Gateway never retries a Lambda invocation. - HTTP API payload 2.0: adds
rawPath,rawQueryString,cookiesandrequestContext.http, and drops the multi-value fields (duplicates are comma-joined). When the function returns valid JSON withoutstatusCode, API Gateway assumes200,content-type: application/jsonand uses the output as the body. The CLI, SDKs and CloudFormation must setpayloadFormatVersionexplicitly. HTTP APIs supportAWS_PROXYandHTTP_PROXYintegrations only;AWS,HTTPandMOCKexist for REST and WebSocket APIs. - Invoke permission: API Gateway needs
lambda:InvokeFunctionthrough the function's resource-based policy (principalapigateway.amazonaws.com,SourceArnset to the API'sexecute-apiARN) or through an IAM role given as the integration credentials. The API Gateway and Lambda consoles and SAM add the statement; with the CLI, SDKs or plain CloudFormation you add it yourself. - A Lambda function in another account can be the integration (its resource-based policy must allow the API), but not one chosen through a stage variable (§5).
- Greedy path: a
{proxy+}resource matches any sub-path, and theANYmethod matches every HTTP verb, so one method can front a whole service. - Lambda custom integration errors: a function error comes back as
200unless an integration responseselectionPattern(a Java-style regex matched againsterrorMessage) maps it to a status code that is also declared as a method response; an undeclared one is a configuration error at runtime. - Per-integration timeout:
timeoutInMillis(REST 50 to 29,000 ms by default). Response streaming (§11) works only onHTTP_PROXYandAWS_PROXY.
Developer angle
# Lambda proxy integration on ANY /orders/{proxy+}
aws apigateway put-integration --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method ANY --type AWS_PROXY --integration-http-method POST \
--uri arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:111122223333:function:orders-api/invocations
# Allow this API (every stage, method and path) to invoke the function
aws lambda add-permission --function-name orders-api --statement-id apigw-orders-invoke \
--action lambda:InvokeFunction --principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:eu-west-1:111122223333:a1b2c3d4e5/*"
# HTTP API first-class integration: put the request body on an SQS queue, no Lambda
aws apigatewayv2 create-integration --api-id x1y2z3 --integration-type AWS_PROXY \
--integration-subtype SQS-SendMessage --payload-format-version 1.0 \
--credentials-arn arn:aws:iam::111122223333:role/apigw-sqs-send \
--request-parameters '{"QueueUrl": "https://sqs.eu-west-1.amazonaws.com/111122223333/orders", "MessageBody": "$request.body"}'
# Map a Lambda custom-integration error message to 400 (400 must also be a method response)
aws apigateway put-integration-response --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method POST --status-code 400 --selection-pattern "Invalid.*"
A Lambda proxy handler that always returns the required shape (body is a string):
import json
def handler(event, context):
order_id = (event.get("pathParameters") or {}).get("proxy", "")
status, payload = (200, {"orderId": order_id}) if order_id else (400, {"message": "order id required"})
return {"statusCode": status, "headers": {"Content-Type": "application/json"}, "body": json.dumps(payload)}
Traps
- Trap: "Keep the Lambda proxy integration and add a mapping template to rename fields." → Reality: REST proxy integrations do not apply integration mapping templates; switch to a custom integration or change the code.
- Trap: "The function threw an exception, so the custom integration returns 500." → Reality: it returns
200unless aselectionPatternmaps the error. - Trap: "502, so the integration timed out." → Reality: 502 means the function ran but failed or returned the wrong shape; an expired integration timeout is 504 (§11).
- Trap: "Return a JSON object as
body." → Reality: in the REST (1.0) formatbodymust be a string (json.dumps). - Trap: "The API built with the CLI returns 500, so the code is broken." → Reality: check the function's resource-based policy first; only the consoles and SAM add it automatically.
Practice questions
-
A developer deploys a REST API with a Lambda proxy integration. Requests reach the function, its logs show it completed successfully, but clients receive
502 Bad Gateway. The function returns{"status": 200, "message": "ok"}. What should the developer change?- A. Increase the integration timeout to 29 seconds.
- B. Add an integration response mapping template that wraps the output in JSON.
- C. Grant
lambda:InvokeFunctiontoapigateway.amazonaws.comon the function. - D. Return
statusCode,headersand a stringbodyfrom the function.
-
A REST API must accept an order and place it on an Amazon SQS queue, returning immediately. There is no business logic, and the solution must have the LEAST operational overhead. Which TWO configuration steps are required? (Select TWO.)
- A. Create a Lambda proxy integration whose function calls
SendMessage. - B. Create an HTTP proxy integration that points at the queue URL.
- C. Create an
AWSintegration for the SQSSendMessageaction with a mapping template that builds the request. - D. Enable caching on the method so repeated orders are not sent twice.
- E. Give API Gateway an IAM role with
sqs:SendMessageon the queue as the integration credentials.
- A. Create a Lambda proxy integration whose function calls
Answers
- D. The output lacks
statusCodeand a stringbody, so API Gateway rejects it as a malformed proxy response. A is irrelevant because the function finished; B does not apply because proxy integrations ignore integration templates; C would produce a 500 before the function runs, but here it ran. - C, E. An
AWSintegration with a template and a role sends the message without any function. A works but adds a function to maintain; B cannot sign requests to SQS, so it would only work against a queue opened to anonymous callers; D does not deduplicate anything and does not apply to POST by default.
4. Mapping templates models and request validation
Concept
- In REST non-proxy integrations, mapping templates written in Velocity Template Language (VTL) rewrite payloads: the integration request template produces what the backend expects, and the integration response template shapes what the client receives. API Gateway chooses the template whose key matches the request
Content-Type, such asapplication/json. WebSocket APIs also run VTL mapping templates on their non-proxy integrations (AWS,HTTP,MOCK), chosen by a template selection expression; HTTP APIs have none (parameter mapping only, below). - Parameter mappings copy or set headers, query strings and path parameters, for example
integration.request.header.X-Tenantfrommethod.request.querystring.tenant, or a static'value'. - Models are JSON Schema (draft 4) documents attached to methods. Request validators combine models with the required-parameter flags and reject bad requests with
400before any backend runs. - Common uses: JSON to XML and back for SOAP services, turning query parameters into a JSON body, trimming large backend responses, injecting
$contextvalues or stage variables, building requests for AWS service actions. - HTTP APIs offer parameter mapping only: append, overwrite or remove request headers and query strings, overwrite the path, and change response headers and the status code; no VTL body transformation and no request validation. ⚠️ Changed: HTTP APIs support parameter mapping. Older material may say HTTP APIs offer no data mapping at all.
Exam facts
- Frequent VTL references:
$input.body(raw payload),$input.json('$.path')(JSON string),$input.path('$.path')(object you can iterate),$input.params('name')(searches path, then query string, then header),$context.requestId,$context.identity.sourceIp,$context.authorizer.claims.email,$context.authorizer.principalId,$stageVariables.name,$util.escapeJavaScript(),$util.parseJson(),$util.urlEncode(),$util.base64Encode(). $context.responseOverride.statusand$context.responseOverride.header.{name}override the method response;$context.requestOverride.header,.pathand.querystringoverride the integration request.- Passthrough behavior when no template matches the request
Content-Type:WHEN_NO_MATCHforwards the body unchanged;WHEN_NO_TEMPLATES(recommended) forwards only when the integration has no templates at all;NEVERrejects even when no template exists. Rejected requests get415 Unsupported Media Type; a request withoutContent-Typeis treated asapplication/json. - Validators: Validate body, Validate query string parameters and headers, or Validate body, query string parameters, and headers. Parameter checks confirm that required parameters are present and not blank (not their type or format). Body checks apply the model for the request's content type, or the
$defaultmodel. Failures return400through the gateway responsesBAD_REQUEST_BODY("Invalid request body") andBAD_REQUEST_PARAMETERS("Missing required request parameters: […]"), and are logged to CloudWatch Logs. - OpenAPI: declare validators in
x-amazon-apigateway-request-validatorsand apply one withx-amazon-apigateway-request-validatorat API level (the default) or on a method (an override). - Limits: mapping template 300 KB; a
#foreachloop runs at most 1,000 iterations; models up to 400 KB per API. Wrap$input.params()values in$util.escapeJavaScript()so client input cannot inject structure into the generated payload. - Binary payloads: declare
binaryMediaTypeson the API;contentHandling(CONVERT_TO_BINARY,CONVERT_TO_TEXT) converts between base64 text and binary.
Developer angle
Integration request template for POST /orders/{id}?customer=c-42 with a JSON body:
#set($body = $input.path('$'))
{
"orderId": "$util.escapeJavaScript($input.params('id'))",
"customerId": "$util.escapeJavaScript($input.params('customer'))",
"itemCount": $body.items.size(),
"requestId": "$context.requestId",
"table": "$stageVariables.ordersTable"
}
Integration response template that keeps two fields of each item:
#set($root = $input.path('$'))
[#foreach($item in $root.items){"id": "$item.id", "status": "$item.status"}#if($foreach.hasNext),#end#end]
# A validator, a model and a POST method that uses both
aws apigateway create-request-validator --rest-api-id a1b2c3d4e5 \
--name body-only --validate-request-body --no-validate-request-parameters
aws apigateway create-model --rest-api-id a1b2c3d4e5 --name OrderModel --content-type application/json \
--schema '{"type": "object", "required": ["sku", "quantity"], "properties": {"sku": {"type": "string"}, "quantity": {"type": "integer", "minimum": 1}}}'
aws apigateway put-method --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 --http-method POST \
--authorization-type NONE --request-validator-id v4l1d8 --request-models application/json=OrderModel
# HTTP API: parameter mapping that adds a header for the backend
aws apigatewayv2 update-integration --api-id x1y2z3 --integration-id i9j8k7 \
--request-parameters '{"append:header.x-request-id": "$context.requestId"}'
Traps
- Trap: "The validator rejects
quantity=abcin the query string." → Reality: parameter validation checks presence only; type checks need a body model or code. - Trap: "A body model validates every request." → Reality: only requests whose
Content-Typematches the model key (or a$defaultmodel) are validated. - Trap: "
WHEN_NO_MATCHis the safe passthrough choice." → Reality: it forwards unexpected content types untransformed;WHEN_NO_TEMPLATESorNEVERreject them with 415. - Trap: "Use an HTTP API to turn JSON into XML for a SOAP service." → Reality: body transformation needs a REST API with VTL, or a function.
- Trap: "Validation failures still invoke the Lambda function." → Reality: API Gateway rejects them with 400 before the integration runs.
Practice questions
-
A REST API fronts a legacy SOAP service that accepts only XML, while clients send JSON. The team does not want to run or maintain any extra compute. What should the developer configure?
- A. An HTTP API with parameter mapping that sets
Content-Type: text/xml - B. A REST API HTTP custom integration with VTL request and response templates
- C. A REST API HTTP proxy integration with the
WHEN_NO_MATCHpassthrough behavior - D. A Lambda proxy integration whose function converts JSON to XML and calls the service
- A. An HTTP API with parameter mapping that sets
-
A POST method must reject requests that lack the
X-Tenant-Idheader or whose JSON body lacks a stringsku, without invoking the backend. Which TWO actions are required? (Select TWO.)- A. Create a model whose JSON schema requires
sku, and attach it to the method forapplication/json. - B. Set the integration passthrough behavior to
NEVER. - C. Mark
X-Tenant-Idas a required method request header and assign a validator that checks the body, query strings and headers. - D. Add an integration request template that sets
skuto an empty string when it is missing. - E. Add a Lambda authorizer that inspects the header and the body.
- A. Create a model whose JSON schema requires
Answers
- B. VTL templates on a REST custom integration convert JSON to XML and back without any compute. A changes a header, not the body; C forwards the JSON unchanged; D works but adds a function to run and maintain.
- A, C. The model checks the body and the validator, with the header marked required, rejects bad requests with 400 before the backend. B only rejects unmapped content types; D transforms rather than rejects and still calls the backend; E fails because authorizers never see the request body.
5. Deployments stages stage variables and canary releases
Concept
- Editing a REST API changes only its working copy. Clients see the change after you deploy: a deployment is an immutable snapshot of resources, methods, integrations, models, templates and authorizers, and a stage (
dev,test,prod,v2) is a named pointer to one deployment plus stage settings (cache, throttling, logging, tracing, WAF, client certificate, stage variables). - The stage name is part of the default URL:
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/…. A rollback is just pointing the stage back at an earlier deployment from its history. - Stage variables are per-stage key/value settings that the API configuration can reference, so one definition can reach different backends per stage. They can fill in an HTTP integration URL, a Lambda function name or alias, an AWS integration credentials ARN, a Cognito user pool ARN, a VPC link ID, mapping templates and parameter mappings, but they can't change the integration type (HTTP proxy in one stage, Lambda in another).
- Stage variables with Lambda aliases: every stage calls
orders-api:${stageVariables.lambdaAlias};devresolves to aliasdevandprodto aliasprod. Version changes and weighted traffic shifting then happen on the alias, with no API change (aliases and weights: seenotes/lambda-guide.md). - Canary release (REST): a stage can carry
canarySettingsthat route a percentage of its traffic to a second deployment, with optional stage variable overrides and optional use of the stage cache. Canary requests get their own log groups and metrics, so the new version is judged before promotion. HTTP APIs have stages (including$defaultat the base URL) with optional automatic deployment, but no canary. - Breaking changes: publish a new stage (
v2) or a new API mapped to/v2on the custom domain sov1clients keep working.
Exam facts
- Redeploy required after changing resources, methods, method or integration responses, integrations, authorizers, models, request validators, gateway responses, documentation, VPC links or the API object itself, including its resource policy.
- No redeploy for stage settings (cache, throttling, logging, variables, canary percentage), usage plans, custom domains and base path mappings, or account settings.
- Stage names: letters, digits, hyphens and underscores, up to 128 characters; 10 stages per API (adjustable). Stage variables: up to 100 per stage; names up to 64 characters (letters, digits, underscore); values up to 512 characters from
[A-Za-z0-9-._~:/?#&=,]. - Stage variables are not meant for secrets: pass sensitive values from a Lambda authorizer's context or read them from Secrets Manager in the backend. Syntax:
${stageVariables.name}inside URIs and ARNs;"$stageVariables.name"(quoted) inside mapping templates;stageVariables.namein parameter mappings. - A Lambda function chosen through a stage variable must live in the same account as the API, and each function or alias the variable can resolve to needs its own resource-based permission; otherwise calls fail with
500 Internal Server Error. - With Lambda proxy, the stage variables arrive in
event['stageVariables']; a custom integration passes them through its mapping template. ⚠️ Changed: stage variables are part of the request event. Older material may say they are passed to the Lambda "context" object. - Canary settings:
percentTrafficfrom 0.0 to 100.0,stageVariableOverrides,useStageCache.$context.isCanaryRequesttells templates and logs which side served a request. A canary can only be added to a stage that already exists. - Promote = copy
canarySettings.deploymentIdto the stage'sdeploymentId, copy the overrides into the stage variables and setpercentTrafficto 0. Promotion leaves the canary settings in place; removecanarySettingsto end the canary. CreateDeploymentis limited to 1 request every 5 seconds per account (not adjustable): pipelines deploying many APIs at once must retryTooManyRequestsException. CloudFormation: anAWS::ApiGateway::DeploymentmustDependsOnthe methods it deploys, otherwise creation fails with "The REST API doesn't contain any methods"; SAM generates deployments for you (notes/sam-guide.md).
Developer angle
# Deploy to prod and set stage variables in one call
aws apigateway create-deployment --rest-api-id a1b2c3d4e5 --stage-name prod \
--description "orders v1.4" --variables lambdaAlias=prod,ordersTable=orders-prod
# Change one stage variable later (takes effect without a redeploy)
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod \
--patch-operations op=replace,path=/variables/ordersTable,value=orders-prod-v2
# Roll back: point the stage at an earlier deployment (IDs from get-deployments)
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod \
--patch-operations op=replace,path=/deploymentId,value=d3p10y
# Canary: send 10 % of prod traffic to a new deployment that uses alias "canary"
aws apigateway create-deployment --rest-api-id a1b2c3d4e5 --stage-name prod \
--canary-settings percentTraffic=10.0,stageVariableOverrides={lambdaAlias=canary},useStageCache=false
# Promote the canary (the canary settings remain, at 0 %)
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod \
--patch-operations '[{"op":"replace","path":"/canarySettings/percentTraffic","value":"0.0"},{"op":"copy","from":"/canarySettings/stageVariableOverrides","path":"/variables"},{"op":"copy","from":"/canarySettings/deploymentId","path":"/deploymentId"}]'
# Integration URI with an alias stage variable, and the invoke permission every alias needs
aws apigateway put-integration --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method GET --type AWS_PROXY --integration-http-method POST \
--uri 'arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:111122223333:function:orders-api:${stageVariables.lambdaAlias}/invocations'
for alias in dev prod canary; do
aws lambda add-permission --function-name "orders-api:${alias}" --statement-id "apigw-${alias}" \
--action lambda:InvokeFunction --principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:eu-west-1:111122223333:a1b2c3d4e5/*/GET/orders"
done
Traps
- Trap: "Clients still get the old behaviour after an integration change, so the cache is stale." → Reality: most often the API was never redeployed to that stage.
- Trap: "A canary and Lambda alias weights do the same thing." → Reality: a canary splits a stage's traffic between two API deployments (API changes included); alias weights split invocations between two function versions behind one integration.
- Trap: "Promoting the canary ends the canary release." → Reality: the settings stay at 0 % until you remove them.
- Trap: "The new alias works because the stage variable resolves to it." → Reality: without
add-permissionfor that alias every call returns 500. - Trap: "Run a canary on the HTTP API." → Reality: canary deployments are REST-only; on HTTP APIs shift traffic with alias weights or separate stages.
Practice questions
-
A REST API integrates with
orders-api:${stageVariables.alias}. The developer creates Lambda aliasblue, sets theprodstage variablealiastoblue, and every call toprodnow fails with500 Internal Server Error. Thedevstage, which uses aliasdev, still works. What is the MOST likely fix?- A. Create a new deployment of the API on the
prodstage. - B. Turn on Lambda proxy integration for the method and redeploy.
- C. Grant
apigateway.amazonaws.cominvoke permission onorders-api:blue. - D. Add
lambda:InvokeFunctionon the alias to the function's execution role.
- A. Create a new deployment of the API on the
-
A developer wants 5 % of production traffic to exercise a new API deployment that also calls a new Lambda alias, compare its error rate with the current version, and then make it the production version. Which TWO actions accomplish this? (Select TWO.)
- A. Create a
prod-newstage and ask 5 % of the clients to switch to its URL. - B. Enable canary settings on the
prodstage withpercentTraffic5.0 and a stage variable override for the alias. - C. Configure weighted routing on the Lambda alias and leave the API unchanged.
- D. Compare the canary's CloudWatch metrics and logs with the stage's, then promote the canary.
- E. Enable automatic deployments on the stage so the canary is promoted when errors drop.
- A. Create a
Answers
- C. Each alias reached through a stage variable needs its own invoke permission;
devworks because its alias has one. A changes nothing because stage variables need no redeploy and the configuration is already deployed; B does not touch permissions; D edits the role the function runs with, which does not control who may invoke it. - B, D. A canary on the stage splits traffic randomly, keeps separate logs and metrics, and is then promoted. A depends on clients choosing URLs; C cannot include the new API deployment; E is an HTTP API feature and never promotes canaries.
6. Caching
Concept
- A REST API stage can provision a dedicated cache. When a request's cache key is present and still within its TTL, API Gateway answers from the cache and the backend is not called, which cuts latency and backend load.
- Turning on the stage cache enables caching for
GETmethods only; other methods need a method-level override. Each method can override caching on or off, its TTL and encryption. - The cache key is the resource path plus any method or integration parameters marked as cache keys (headers, query strings, path parameters). Parameters left out of the key are ignored, so
?type=adminand?type=usershare one entry unlesstypeis a key. - Invalidation: flush the whole stage cache (console or
flush-stage-cache), or let an authorized client refresh a single entry by sendingCache-Control: max-age=0. - The cache is billed per hour by size and is outside the Free Tier, so teams usually provision it only on production stages.
Exam facts
- TTL: default 300 s, configurable from 0 to 3,600 s; a TTL of 0 disables caching.
- Capacity: 0.5, 1.6, 6.1, 13.5, 28.4, 58.2, 118 or 237 GB. Provisioning takes up to about 4 minutes; changing the size deletes the cache and builds a new one, so every entry is lost. Largest cacheable response: 1,048,576 bytes (encryption overhead counts toward it).
- Caching is best effort. Measure it with
CacheHitCountandCacheMissCount(theirAverageis the hit or miss rate); CloudFront'sX-Cacheheader says nothing about the API cache. - Per-entry invalidation needs
execute-api:InvalidateCacheon the method ARN when Require authorization (requireAuthorizationForCacheControl) is on. Without it any client can force cache misses. Handling of unauthorized attempts (unauthorizedCacheControlHeaderStrategy):FAIL_WITH_403,SUCCEED_WITH_RESPONSE_HEADER(header ignored, warning header added) orSUCCEED_WITHOUT_RESPONSE_HEADER. Cross-account invalidation is not supported. - Canary requests can share the stage cache (
useStageCache); production and canary responses are cached separately while they run different deployments. - Streamed responses (§11) are never cached. HTTP APIs have no cache: put CloudFront in front (see
notes/cloudfront-guide.md). Lambda authorizer decisions use a separate cache with their own TTL (§9).
Developer angle
# Provision a 0.5 GB cache on prod and enable caching for its GET methods
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod --patch-operations \
op=replace,path=/cacheClusterEnabled,value=true op=replace,path=/cacheClusterSize,value=0.5 \
op=replace,path=/*/*/caching/enabled,value=true
# GET /products: 60 s TTL, encrypted entries, authorization required for Cache-Control
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod \
--patch-operations op=replace,path=/~1products/GET/caching/ttlInSeconds,value=60 \
op=replace,path=/~1products/GET/caching/dataEncrypted,value=true \
op=replace,path=/~1products/GET/caching/requireAuthorizationForCacheControl,value=true \
op=replace,path=/~1products/GET/caching/unauthorizedCacheControlHeaderStrategy,value=FAIL_WITH_403
# Make the "category" query string part of the cache key
aws apigateway put-method --rest-api-id a1b2c3d4e5 --resource-id p7r8d9 --http-method GET \
--authorization-type NONE --request-parameters "method.request.querystring.category=false"
aws apigateway put-integration --rest-api-id a1b2c3d4e5 --resource-id p7r8d9 --http-method GET \
--type HTTP_PROXY --integration-http-method GET --uri 'https://catalog.example.com/products' \
--request-parameters "integration.request.querystring.category=method.request.querystring.category" \
--cache-key-parameters "method.request.querystring.category"
# Empty the stage cache after a bulk data change
aws apigateway flush-stage-cache --rest-api-id a1b2c3d4e5 --stage-name prod
Identity policy that lets a back-office role refresh cached entries (it then sends a SigV4-signed GET /products with Cache-Control: max-age=0):
{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": "execute-api:InvalidateCache",
"Resource": "arn:aws:execute-api:eu-west-1:111122223333:a1b2c3d4e5/prod/GET/products"}]}
Traps
- Trap: "The stage cache now caches POST responses too." → Reality: only GET is cached by default; other methods need an explicit override, which is rarely wise.
- Trap: "Users see each other's results after the cache was enabled." → Reality: the parameter that varies (a query string, or a header such as
Authorization) is missing from the cache key. - Trap: "Resize the cache at peak time to raise the hit rate." → Reality: resizing recreates the cache empty.
- Trap: "Clients sending
Cache-Control: max-age=0are harmless." → Reality: unless authorization is required for it, anyone can bypass the cache and load the backend. - Trap: "Enable caching on the HTTP API stage." → Reality: HTTP APIs have no cache.
Practice questions
-
A catalog REST API filters results with the
categoryquery string. After caching was enabled on the stage, users browsingcategory=gardensometimes receive kitchen products. The team wants to keep the benefit of caching. What should the developer do?- A. Add
categoryas a cache key parameter on the method's integration. - B. Reduce the stage cache TTL to 0 seconds for every method in the stage.
- C. Flush the stage cache with
flush-stage-cacheevery hour. - D. Switch the method to a Lambda proxy integration.
- A. Add
-
A REST API caches
GET /pricesfor 300 seconds. A pricing service must refresh a single cached entry right after each price change, and no other client may bypass the cache. Which TWO actions meet these requirements? (Select TWO.)- A. Have the pricing service call
flush-stage-cacheafter each change. - B. Raise the method's TTL to 3,600 seconds so entries expire less often.
- C. Put public clients on a usage plan with a lower quota.
- D. Require authorization for cache invalidation and use the
FAIL_WITH_403strategy. - E. Grant the pricing service's role
execute-api:InvalidateCacheand send signed requests withCache-Control: max-age=0.
- A. Have the pricing service call
Answers
- A. Adding
categoryto the cache key stores each category separately. B removes caching altogether; C still mixes categories between flushes; D changes the integration type, not the cache key. - D, E. Requiring authorization blocks other clients, and the permitted role refreshes one entry with
Cache-Control: max-age=0. A empties the whole cache; B delays freshness; C limits volume but still lets anyone bypass the cache.
7. Throttling usage plans and API keys
Concept
- API Gateway throttles with a token bucket: the rate is how many requests per second refill the bucket, and the burst is the bucket's size, the number of requests that can arrive at once. Requests beyond what the bucket allows get
429 Too Many Requests. - Limits exist at several levels and are applied from the most specific: per-client and per-method limits in a usage plan → per-method limits on the stage → the account limit for the Region → AWS Regional limits. Stage, method and per-client limits can't be set above the account limit.
- Because the account limit is a shared pool, a traffic spike on one API can cause 429s on unrelated APIs in the same account and Region; stage and method limits contain the damage.
- A usage plan binds API stages (optionally with per-method throttles) to a throttle (rate, burst) and a quota (requests per day, week or month) that apply to each API key associated with the plan: it is how an API is metered or sold per customer.
- API keys identify clients for usage plans; they do not authenticate anyone. Clients send them in the
x-api-keyheader, or a Lambda authorizer supplies them (apiKeySource = AUTHORIZER,usageIdentifierKeyin the authorizer output). - Throttles and quotas are targets enforced on a best-effort basis. Don't rely on them to cap spend or block access: use AWS Budgets for cost and AWS WAF or authorizers for blocking. HTTP APIs offer account, stage-default and per-route throttling, but no usage plans and no API keys.
Exam facts
- Account default: 10,000 requests per second with a burst of 5,000, shared by REST, HTTP and WebSocket APIs (and WebSocket callbacks) per account per Region. ⚠️ Changed: 10,000 rps with a 5,000 burst, except 13 Regions (for example Africa (Cape Town), Europe (Milan), Asia Pacific (Jakarta), Middle East (UAE), Europe (Spain), Mexico (Central)) that default to 2,500 rps with a 1,250 burst; the rate is adjustable (Service Quotas
L-8A5B8E43), the burst is set by AWS. Older material may say 10,000 rps everywhere, with no mention of burst. - Setup order: (1) mark the methods as key-required and deploy, (2) issue keys (generated by API Gateway or imported from CSV), (3) define the plan's throttle and quota, (4) attach the API stages and the keys to the plan.
- Missing or invalid key on a key-required method →
403 Forbidden(gateway responseINVALID_API_KEYfor a wrong key); quota used up →429(QUOTA_EXCEEDED); rate exceeded →429(THROTTLED). - API key values: 20 to 128 characters and unique; names up to 1,024 characters. 10,000 API keys per account per Region (fixed); 300 usage plans per account per Region; 10 usage plans per API key; a key can belong to only one usage plan per stage.
- Quota
period:DAY,WEEKorMONTH, with an optionaloffset. Throttles and quotas count per key across all stages in the plan. - A valid key for one API in a plan works for every API in that plan: keep unrelated APIs in separate plans. Adding a key to a plan can take a few minutes to take effect.
- Clients should retry 429 responses with exponential backoff and jitter (see
notes/cli-sdk-policies-guide.md). WebSocket APIs can require an API key on the$connectroute.
Developer angle
# Stage-wide default limits plus a tighter limit for one expensive method
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod --patch-operations \
op=replace,path=/*/*/throttling/rateLimit,value=500 op=replace,path=/*/*/throttling/burstLimit,value=1000 \
op=replace,path=/~1reports/POST/throttling/rateLimit,value=20
# "basic" tier: 10 rps, burst 20, 50,000 requests per month
aws apigateway create-usage-plan --name basic --api-stages apiId=a1b2c3d4e5,stage=prod \
--throttle burstLimit=20,rateLimit=10 --quota limit=50000,offset=0,period=MONTH
# Issue a key to a customer and attach it to the plan
aws apigateway create-api-key --name customer-acme --enabled
aws apigateway create-usage-plan-key --usage-plan-id u5p1an --key-id k3y1d2 --key-type API_KEY
# Require the key on a method (then redeploy the API)
aws apigateway update-method --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method GET --patch-operations op=replace,path=/apiKeyRequired,value=true
# Usage so far this month (the client sends the key as x-api-key)
aws apigateway get-usage --usage-plan-id u5p1an --key-id k3y1d2 \
--start-date 2026-10-01 --end-date 2026-10-12
# HTTP API: per-route throttling on a stage
aws apigatewayv2 update-stage --api-id x1y2z3 --stage-name '$default' \
--route-settings '{"POST /orders": {"ThrottlingBurstLimit": 200, "ThrottlingRateLimit": 100}}'
Traps
- Trap: "Use API keys to authenticate partners." → Reality: keys travel in headers and get logged or shared; authenticate with IAM, Cognito or a Lambda authorizer and keep keys for plans and metering.
- Trap: "The usage plan quota caps the monthly bill." → Reality: quotas are best effort; use AWS Budgets and WAF.
- Trap: "Ask AWS to raise the burst to 20,000." → Reality: only the rate can be requested; the burst follows from it.
- Trap: "Associating the key with the plan is enough." → Reality: the method must also require an API key, and the API must be redeployed.
- Trap: "429 means the backend is overloaded." → Reality: API Gateway refused the request before it reached the backend.
Practice questions
-
A SaaS company sells its REST API in two tiers. Free-tier customers may send 1,000 requests per day at no more than 5 requests per second; paid customers get higher limits. Which solution meets these requirements with the LEAST operational overhead?
- A. Track each customer's requests in DynamoDB from a Lambda authorizer and deny calls over the limit.
- B. Set method throttling on the stage to 5 requests per second for every client.
- C. Create one stage per customer and configure stage-level throttling on each.
- D. Create a usage plan per tier with its throttle and daily quota, and attach each customer's API key.
-
During month-end, a reporting API returns bursts of
429 Too Many Requestswhile CloudWatch shows its backend idle. Other APIs in the same account and Region are throttled at the same moments. Which TWO actions address the problem? (Select TWO.)- A. Set stage or method throttling on the reporting API so it cannot consume the whole account limit.
- B. Increase the memory size of the backend Lambda function.
- C. Enable API caching on the reporting API's POST methods.
- D. Change the API to an edge-optimized endpoint.
- E. Request a higher account-level throttle quota for the Region in Service Quotas.
Answers
- D. Usage plans give each tier its own throttle and daily quota, enforced per API key with no code. A needs custom code and a table; B applies one limit to everyone; C does not scale and runs into the stages-per-API quota.
- A, E. Capping the reporting API protects the shared pool, and a higher Regional quota adds headroom. B targets a backend that is idle; C does not stop throttling and POST responses are not cached by default; D does not change account limits.
8. IAM authorization and resource policies
Concept
- With
AWS_IAMauthorization on a method (REST) or route (HTTP and WebSocket), callers sign requests with Signature Version 4 using AWS credentials and the service nameexecute-api; API Gateway checks their IAM permissions forexecute-api:Invokeon the method's ARN. It suits callers that already hold AWS credentials: roles on EC2, ECS or Lambda, principals in other accounts, users with Cognito identity pool credentials. SigV4 details: seenotes/cli-sdk-policies-guide.md. - A resource policy (REST only) is a JSON policy attached to the API. It allows or denies
execute-api:Invokeby principal (accounts, roles, users), source IP range, VPC or VPC endpoint (private APIs), time and other conditions, and it combines with any authorization type. - The outcome depends on where the caller lives. Same account: an allow in either the caller's IAM policy (or a Lambda authorizer's policy) or the resource policy is enough, unless something explicitly denies. Different account: the caller's IAM policy (or the Cognito authorizer) and the resource policy must both explicitly allow. General policy evaluation: see
notes/advanced-identity-guide.md. - AWS WAF (REST API stages) is evaluated before resource policies and authorizers, and is the tool for managed rule groups (SQL injection, XSS), IP reputation, geo blocking and rate-based rules.
execute-apiactions:Invoke(call the API),InvalidateCache(refresh cache entries, §6),ManageConnections(WebSocket@connections, §13).
Exam facts
- Method ARN:
arn:aws:execute-api:{region}:{account-id}:{api-id}/{stage}/{HTTP-VERB}/{resource-path};*can stand in for any part (a1b2c3d4e5/*/GET/*). A method ARN longer than 1,600 bytes returns414 Request URI too long. - IAM policies have no effect on a method whose authorization is
NONE: it stays public until it usesAWS_IAM. - A resource policy that names AWS principals requires
AWS_IAMon the methods; otherwise callers seeUser: anonymous is not authorized to perform: execute-api:Invoke. - A changed resource policy takes effect only after a redeploy. Editing it needs
apigateway:UpdateRestApiPolicyandapigateway:PATCH. Size limit 8,192 characters (adjustable): use CIDR ranges, or move IP filtering to WAF. - Resource policy with authorization
NONE: the policy alone decides and needs an explicit allow, so a deny-only policy blocks everyone. With a Lambda authorizer, API Gateway checks the resource policy for an explicit deny before invoking the authorizer. - Condition keys:
aws:SourceIp,aws:SecureTransport,aws:CurrentTime,aws:UserAgentandaws:Refererwork with every authorization type;aws:SourceVpc,aws:SourceVpceandaws:VpcSourceIponly on private APIs;aws:PrincipalArn,aws:PrincipalAccount,aws:PrincipalOrgIDandaws:PrincipalTagonly with IAM authorization. - Typical 403 bodies: unsigned call to an IAM method → "Missing Authentication Token"; bad signature →
INVALID_SIGNATURE; expired credentials →EXPIRED_TOKEN; policy denies → "User: … is not authorized to perform: execute-api:Invoke"; WAF block →WAF_FILTERED. - WAF needs a Regional WAFv2 web ACL in the API's Region, associated with the stage ARN
arn:aws:apigateway:{region}::/restapis/{api-id}/stages/{stage}; it inspects the first 16 KB of a body by default, configurable up to 64 KB (extra cost). - Backend authentication: a stage can present an API Gateway-generated client certificate (self-signed, valid 365 days,
generate-client-certificate) to its HTTP integrations, so the backend can accept only calls from API Gateway (REST and WebSocket APIs). - Cognito identity pools trade user sign-ins for temporary AWS credentials, which lets app users call
AWS_IAMmethods (seenotes/advanced-identity-guide.md).
Developer angle
# Require SigV4 on a REST method (redeploy afterwards)
aws apigateway update-method --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method GET --patch-operations op=replace,path=/authorizationType,value=AWS_IAM
# Create an API with a resource policy read from a file
aws apigateway create-rest-api --name partner-api \
--endpoint-configuration types=REGIONAL --policy file://resource-policy.json
Identity policy for a caller that may read orders in prod only:
{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:eu-west-1:111122223333:a1b2c3d4e5/prod/GET/orders/*"}]}
resource-policy.json: a partner account's role may read orders, but only from the partner's network range:
{"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::444455556666:role/partner-integration"},
"Action": "execute-api:Invoke", "Resource": "execute-api:/prod/GET/orders/*"},
{"Effect": "Deny", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "execute-api:/*",
"Condition": {"NotIpAddress": {"aws:SourceIp": ["198.51.100.0/24"]}}}]}
Callers sign with their SDK's SigV4 signer (service name execute-api), or test with a signing client such as awscurl --service execute-api https://a1b2c3d4e5.execute-api.eu-west-1.amazonaws.com/prod/orders/123.
Traps
- Trap: "The IAM policies deny everyone else, so the API is protected." → Reality: with authorization
NONE, IAM is never consulted. - Trap: "Cross-account access works with the resource policy alone." → Reality: under IAM authorization, the caller's own identity policy must allow
execute-api:Invoketoo. - Trap: "Add
aws:SourceVpceto a Regional API to keep it in-VPC." → Reality: the VPC keys apply to private APIs only. - Trap: "The resource policy was saved, so it is live." → Reality: redeploy the API.
- Trap: "403 always means IAM said no." → Reality: it can also be a missing or invalid API key, a WAF block, a wrong path ("Missing Authentication Token") or a disabled default endpoint.
Practice questions
-
Account A hosts a REST API whose methods use
AWS_IAMauthorization. A Lambda function in account B, running as roleingest-role, must call it. Account A's resource policy already allowsingest-role, yet the calls return 403. What else is required?- A. Replace
AWS_IAMwith a Cognito user pool authorizer on each method of the API. - B. Allow
execute-api:Invokein an identity policy attached toingest-role. - C. Associate account B with a usage plan and give it an API key.
- D. Enable CORS on the API for the origin of the Lambda function in account B.
- A. Replace
-
A REST API must accept calls only from a corporate IP range and must block common SQL injection patterns with managed rules, with minimal custom code. Which TWO actions should the developer take? (Select TWO.)
- A. Add a resource policy that allows
execute-api:Invokeand denies it whenaws:SourceIpis outside the corporate range. - B. Add a Lambda authorizer that scans the request body for SQL keywords.
- C. Associate an AWS WAF web ACL with managed SQL injection rules to the API stage.
- D. Add an
aws:SourceVpcecondition that names the corporate network. - E. Create a usage plan with a low quota for unknown callers.
- A. Add a resource policy that allows
Answers
- B. Cross-account IAM access needs an allow on both sides: the resource policy in A and an identity policy in B. A expects user pool tokens, not role credentials; C does not grant access; D only matters to browsers.
- A, C. A resource policy that allows calls but denies other source IPs, plus a WAF web ACL with managed rules, covers both needs without code. B is custom code, and authorizers never see the body; D only works on private APIs; E limits volume, not attacks.
9. Cognito JWT and Lambda authorizers
Concept
- Cognito user pool authorizer (REST, type
COGNITO_USER_POOLS): the client signs in to a user pool, receives an ID token and an access token, and sends one of them in a header (usuallyAuthorization); API Gateway verifies it against the pool with no code. Without OAuth scopes on the method the token is treated as an ID token; once scopes are configured, an access token carrying one of those scopes is required. Claims reach the backend as$context.authorizer.claims.{name}(orrequestContext.authorizer.claimsin a proxy event). User pool setup: seenotes/advanced-identity-guide.md. - JWT authorizer (HTTP APIs): validates JWTs from any OIDC or OAuth 2.0 issuer, Cognito included, using the
issuerandaudienceyou configure plus the route's scopes. - Lambda authorizer (REST, HTTP and WebSocket
$connect): your function receives the token or request data and returns a decision. REST offersTOKEN(one header holding a bearer token such as a JWT or OAuth token, with an optional regex pre-check) andREQUEST(any mix of headers, query strings, stage variables and$contextvalues; the recommended type). The function returns aprincipalIdand an IAM policy forexecute-api:Invoke, optionally acontextmap and ausageIdentifierKey; decisions are cached per identity for a TTL, so most requests don't invoke the function again. - Choosing: callers with AWS credentials → IAM; your own user pool, no code → Cognito authorizer (REST) or JWT authorizer (HTTP); third-party tokens or custom rules (tenant checks, database lookups, several inputs) → Lambda authorizer.
Exam facts
- REST Lambda authorizer cache (
authorizerResultTtlInSeconds): default 300 s, maximum 3,600 s,0turns caching off. Cache key: the token header (TOKEN) or every identity source in order (REQUEST). The cached policy is reused for other methods of the same API, so return a policy that covers every method the caller may use, add$context.pathand$context.httpMethodto the identity sources, or disable caching. REQUESTauthorizer with caching on: a missing or empty identity source gets401without invoking the function.- Outcomes: policy with
Deny→403 Forbidden; the function raisesUnauthorized→401; any other function error or malformed output →500. - REST
contextvalues must be strings, numbers or booleans and arrive as strings; objects and arrays are rejected. API Gateway needs permission to invoke the authorizer function: a resource-based policy (added by the console) or an IAM role set inauthorizerCredentials. - HTTP API Lambda authorizers declare
authorizerPayloadFormatVersion1.0or2.0. ⚠️ Changed: with format 2.0 and simple responses enabled, the function can return{"isAuthorized": true, "context": {…}}instead of a policy. Older material may say every Lambda authorizer must return an IAM policy. The cache key is the identity sources (add$context.routeKeyto cache per route), the TTL maximum is 3,600 s and the function must answer within 10,000 ms. - JWT authorizer checks: signature with the RSA keys from the issuer's
jwks_uri(cached for up to 2 hours),kid,iss,aud(orclient_idwhenaudis absent),exp,nbf,iat, andscopeorscpagainst the route's scopes. Limits: 50 audiences per authorizer, 10 scopes per route. Claims reach Lambda atevent['requestContext']['authorizer']['jwt']['claims']. - Cognito authorizer: up to 1,000 user pools per authorizer (fixed); its Token validation regex is matched against
aud, a claim access tokens don't carry, so it rejects access tokens; a scope mismatch returns401. 10 authorizers per API (raise through AWS Support).
Developer angle
# REST: Cognito user pool authorizer, then require a scope on a method (access tokens)
aws apigateway create-authorizer --rest-api-id a1b2c3d4e5 --name orders-users --type COGNITO_USER_POOLS \
--provider-arns arn:aws:cognito-idp:eu-west-1:111122223333:userpool/eu-west-1_AbCdEf123 \
--identity-source method.request.header.Authorization
aws apigateway update-method --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 --http-method GET --patch-operations \
op=replace,path=/authorizationType,value=COGNITO_USER_POOLS op=replace,path=/authorizerId,value=c0gn1t \
op=add,path=/authorizationScopes,value=orders/read
# REST: REQUEST Lambda authorizer, decisions cached 5 minutes per (Authorization header, tenant)
aws apigateway create-authorizer --rest-api-id a1b2c3d4e5 --name tenant-check --type REQUEST \
--authorizer-uri arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:111122223333:function:tenant-authorizer/invocations \
--identity-source 'method.request.header.Authorization,method.request.querystring.tenant' --authorizer-result-ttl-in-seconds 300
# HTTP API: JWT authorizer for a Cognito user pool, attached to a route with a scope
aws apigatewayv2 create-authorizer --api-id x1y2z3 --name cognito-jwt --authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration Audience=4exampleclientid,Issuer=https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_AbCdEf123
aws apigatewayv2 update-route --api-id x1y2z3 --route-id r0ut31 --authorization-type JWT --authorizer-id j1w2t3 --authorization-scopes orders/read
A REST REQUEST authorizer in Python whose policy covers the whole stage, so a cached decision fits every method:
def handler(event, context):
token = (event.get("headers") or {}).get("Authorization", "")
tenant = (event.get("queryStringParameters") or {}).get("tenant", "")
api_arn, stage = event["methodArn"].split("/")[0:2] # arn:...:{api-id} and {stage}
allowed = token.startswith("Bearer ") and tenant != "" # placeholder for real token checks
statement = {"Action": "execute-api:Invoke", "Effect": "Allow" if allowed else "Deny",
"Resource": f"{api_arn}/{stage}/*"}
return {"principalId": tenant or "anonymous",
"policyDocument": {"Version": "2012-10-17", "Statement": [statement]},
"context": {"tenant": tenant, "plan": "gold"}}
Traps
- Trap: "A Cognito user pool authorizer gives fine-grained, per-record authorization." → Reality: it authenticates the user; method access is coarse (scopes), and per-record checks stay in the backend or a Lambda authorizer.
- Trap: "Add a token validation regex and send access tokens." → Reality: the regex is matched against
aud, which access tokens lack. - Trap: "Caching is on, and users get 403 on a second method they are entitled to." → Reality: the cached policy listed only the first method's ARN; widen the policy or change the cache key.
- Trap: "A
TOKENauthorizer can combine a header with a query string." → Reality:TOKENreads one header; useREQUESTfor several identity sources. - Trap: "The Lambda authorizer can validate the request body." → Reality: authorizers receive headers, query strings, path, stage variables and context, never the body.
Practice questions
-
A REST API must accept OAuth 2.0 access tokens issued by a third-party identity provider and allow access only when the token's
tenantclaim matches the tenant segment of the request path. Decisions must be cached per token and path. Which authorizer should the developer use?- A. A
REQUESTLambda authorizer with theAuthorizationheader and$context.pathas identity sources - B. A Cognito user pool authorizer with the provider configured as a federated identity provider
- C.
AWS_IAMauthorization with a resource policy condition on the tenant - D. A Lambda authorizer of type
TOKENthat reads theAuthorizationheader
- A. A
-
A REST API uses a Lambda authorizer with a 300-second result cache. After a successful
GET /orders, users receive 403 onGET /invoicesfor a few minutes, although they are entitled to both. Which TWO changes fix this? (Select TWO.)- A. Raise the authorizer result TTL to 3,600 seconds.
- B. Change the authorizer type from
REQUESTtoTOKEN. - C. Return a policy whose
Resourcecovers every method the user is entitled to call. - D. Set the authorizer result TTL to 0 so each request is authorized on its own.
- E. Enable API caching on both methods.
Answers
- A. A
REQUESTauthorizer receives the token and the path, and with both as identity sources its cache key covers both. B cannot compare a claim with a path parameter, and federation still issues Cognito tokens rather than accepting the provider's; C requires AWS credentials the callers do not have; D caches on the token alone, so one tenant's decision would be reused for another tenant's path. - C, D. Either make the cached policy cover all entitled methods, or stop caching. A keeps the wrong decision longer; B caches on the token alone, with the same problem; E caches responses, not authorization.
10. CORS
Concept
- Browsers stop JavaScript from reading responses from a different origin (scheme, host or port) unless the server permits it with CORS headers. Server-to-server calls, curl and mobile SDKs ignore CORS entirely.
- Simple requests (GET, HEAD or POST with basic content types and no custom headers) go straight out and only need
Access-Control-Allow-Originon the response. Anything else (PUT, DELETE,application/jsonbodies,Authorizationorx-api-keyheaders) triggers a preflight: the browser first sendsOPTIONSwithOrigin,Access-Control-Request-MethodandAccess-Control-Request-Headers, and only proceeds if the response permits them. - REST non-proxy integrations: Enable CORS in the console adds an
OPTIONSmethod backed by a MOCK integration that returnsAccess-Control-Allow-Methods,Access-Control-Allow-HeadersandAccess-Control-Allow-Origin, and addsAccess-Control-Allow-Originto the real method's 200 response. Redeploy afterwards. - REST proxy integrations (Lambda proxy or HTTP proxy): the backend owns the response, so it must return
Access-Control-Allow-Origin, and the preflight headers too whenOPTIONSreaches it (for example throughANY /{proxy+}). - Errors that API Gateway generates (401, 403, 429 and 5xx gateway responses) carry no CORS headers unless you add them to the gateway responses (
DEFAULT_4XX,DEFAULT_5XX); otherwise the browser reports a CORS error that hides the real status. - HTTP APIs: configure
corsConfigurationonce; API Gateway answers preflight requests itself (noOPTIONSroute needed) and adds the headers to integration responses, ignoring any CORS headers the backend sends.
Exam facts
- ⚠️ Changed:
Access-Control-Allow-Methods,Access-Control-Allow-HeadersandAccess-Control-Allow-Originare headers of the response to the preflightOPTIONSrequest. Older material may say the OPTIONS preflight request must contain them. - Console defaults:
Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'andAccess-Control-Allow-Origin: '*'. Static header values in REST mappings are single-quoted. - Enable CORS is not recursive (child resources need their own), and on an
ANYmethod with a proxy integration it sets nothing: the backend must return the headers. AWS recommends passthroughNEVERon theOPTIONSmock integration. - HTTP API
corsConfigurationfields:allowOrigins,allowMethods,allowHeaders,exposeHeaders,maxAge,allowCredentials. When an authorizer protects the$defaultroute, add anOPTIONS /{proxy+}route without authorization (with an integration attached); it outranks$default, so preflights are not rejected. - Browsers refuse
Access-Control-Allow-Origin: *on credentialed requests (cookies, orAccess-Control-Allow-Credentials: true): return the specific origin instead. - CORS is not access control: it only tells browsers which web origins may read responses, so other websites and scripts can still call the API; protect it with authorizers. S3 bucket CORS: see
notes/s3-guide.md.
Developer angle
Lambda proxy function that answers both the preflight and the real request:
import json
CORS_HEADERS = {"Access-Control-Allow-Origin": "https://app.example.com",
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
"Access-Control-Allow-Headers": "Content-Type,Authorization"}
def handler(event, context):
if event["httpMethod"] == "OPTIONS":
return {"statusCode": 204, "headers": CORS_HEADERS, "body": ""}
return {"statusCode": 200, "headers": CORS_HEADERS, "body": json.dumps({"ok": True})}
Gateway responses that keep CORS headers on API Gateway's own errors (OpenAPI extension at the API root):
{"x-amazon-apigateway-gateway-responses": {
"DEFAULT_4XX": {"responseParameters": {"gatewayresponse.header.Access-Control-Allow-Origin": "'https://app.example.com'"}},
"DEFAULT_5XX": {"responseParameters": {"gatewayresponse.header.Access-Control-Allow-Origin": "'https://app.example.com'"}}}}
HTTP API CORS configuration and a preflight check with curl:
aws apigatewayv2 update-api --api-id x1y2z3 --cors-configuration \
'{"AllowOrigins": ["https://app.example.com"], "AllowMethods": ["GET", "POST"], "AllowHeaders": ["authorization", "content-type"], "MaxAge": 300}'
curl -i -X OPTIONS https://x1y2z3.execute-api.eu-west-1.amazonaws.com/orders \
-H 'Origin: https://app.example.com' -H 'Access-Control-Request-Method: POST'
Traps
- Trap: "CORS is enabled in the console, but the Lambda proxy responses still fail in the browser." → Reality: with proxy integrations the function must return
Access-Control-Allow-Origin. - Trap: "CORS works for 200s, yet failed logins show a CORS error." → Reality: API Gateway's 401/403 gateway responses lack the headers; add them to
DEFAULT_4XX. - Trap: "CORS was enabled and nothing changed." → Reality: REST APIs need a redeploy.
- Trap: "curl works, so CORS is fine." → Reality: curl ignores CORS; send an
Originheader or test in the browser. - Trap: "The HTTP API returns the backend's CORS headers." → Reality: with
corsConfigurationset, API Gateway ignores them.
Practice questions
-
A single-page app on
https://shop.example.comcalls a REST API that uses a Lambda proxy integration. The developer enabled CORS in the console and redeployed, but the browser still reports a missingAccess-Control-Allow-Originheader onPOST /cartresponses. What should the developer do?- A. Change the method to an HTTP proxy integration and redeploy the API.
- B. Add
Access-Control-Allow-Originto theDEFAULT_5XXgateway response of the API. - C. Return the
Access-Control-Allow-Originheader from the Lambda function. - D. Allow the
https://shop.example.comorigin in the API's resource policy.
-
A web app calls an HTTP API with an
Authorizationheader. Preflight requests fail with 401 because a JWT authorizer protects the$defaultroute. Which TWO actions resolve this with the LEAST effort? (Select TWO.)- A. Set
corsConfigurationon the API with the app's origin, its methods and theauthorizationheader. - B. Add an
OPTIONS /{proxy+}route with an integration and no authorizer. - C. Return CORS headers from the backend Lambda function on every route.
- D. Remove the JWT authorizer and validate tokens in the function.
- E. Rebuild the API as a REST API with a MOCK
OPTIONSmethod.
- A. Set
Answers
- C. With a proxy integration the function's response is what the browser receives, so the function must add the header. A keeps the same responsibility on the backend; B only covers error responses; D is about authorization, not CORS.
- A, B. API Gateway answers preflights from
corsConfiguration, and an unauthenticatedOPTIONS /{proxy+}route keeps the authorizer away from them. C does not help because configured CORS ignores backend headers, and the preflight is rejected before the backend; D weakens security and adds code; E is a migration.
11. Error codes timeouts and payload limits
Concept
- First establish who produced the error. API Gateway returns gateway responses when it rejects or fails a request itself (authorization, throttling, validation, timeouts); any other status came from the backend and was passed through. Access log fields such as
$context.error.responseType,$context.integration.statusand$context.integration.errorseparate the two. - Every gateway response type has a default status code and can be customized per API (status, headers, body template), for example to add CORS headers or a clearer message.
DEFAULT_4XXandDEFAULT_5XXcover the types you don't set one by one. Customizations need a redeploy. - When the integration timeout expires (29 s REST default, 30 s HTTP, 29 s WebSocket), the caller gets an error (
504on REST APIs) but the backend, such as a Lambda function with a 15-minute timeout, may keep running. Long work should not hold the request open: start an asynchronous job (async Lambda, SQS, Step Functions) and return an ID to poll, push the result over WebSocket, stream the response (REST), or raise the integration timeout of a Regional or private REST API.
Exam facts
| Status | Typical gateway response or cause | First thing to check |
|---|---|---|
| 400 | BAD_REQUEST_BODY, BAD_REQUEST_PARAMETERS |
Request validator and model |
| 401 | UNAUTHORIZED: Cognito or Lambda authorizer rejected the caller, or an identity source is missing |
Token present, right header, scopes |
| 403 | ACCESS_DENIED (authorizer or IAM deny), INVALID_API_KEY, MISSING_AUTHENTICATION_TOKEN (also an unknown path, method or stage), INVALID_SIGNATURE, EXPIRED_TOKEN, WAF_FILTERED, disabled default endpoint |
URL path and stage, authorization type, API key, WAF logs |
| 404 | RESOURCE_NOT_FOUND after authentication and authorization passed |
Resource exists in the deployed snapshot |
| 413 | REQUEST_TOO_LARGE: body over 10 MB |
Upload to S3 with a presigned URL |
| 414 | Method ARN over 1,600 bytes (very long path parameters) | Move long values to headers or the body |
| 415 | UNSUPPORTED_MEDIA_TYPE: passthrough NEVER or WHEN_NO_TEMPLATES |
Content-Type and mapping templates |
| 429 | THROTTLED (account, stage, method or plan), QUOTA_EXCEEDED (usage plan) |
Throttle settings; retry with backoff |
| 500 | API_CONFIGURATION_ERROR, AUTHORIZER_FAILURE, AUTHORIZER_CONFIGURATION_ERROR; Lambda refused the invocation (missing invoke permission, Lambda throttling) |
Lambda resource policy, authorizer output, response mappings |
| 502 | Lambda proxy: the function failed or returned malformed output | Function errors in its logs; statusCode and a string body |
| 503 | No REST gateway response type defaults to 503: it usually comes from the backend, passed through a proxy integration or mapped by an integration response | Backend health; retry with backoff |
| 504 | INTEGRATION_TIMEOUT or INTEGRATION_FAILURE: no backend answer in time |
Backend latency vs timeoutInMillis |
- Integration timeout: ⚠️ Changed: REST integrations default to 50 ms–29 s; since June 2024, Regional and private REST APIs can go above 29 s through a Service Quotas increase (
L-E5AE38E3), possibly in exchange for a lower account throttle quota; edge-optimized REST APIs stay at 29 s (switch to Regional for more); HTTP APIs top out at 30 s (fixed); WebSocket integrations at 29 s. Older material may say 29 s is a hard limit for every API. - Payload: 10 MB request and response for REST and HTTP APIs, not adjustable.
- Response streaming: ⚠️ Changed: since 2025-11-19, REST APIs on every endpoint type can set
responseTransferMode: STREAM(defaultBUFFERED) onHTTP_PROXYandAWS_PROXYintegrations; a stream can last up to 15 minutes (beyond 29 s without a timeout quota increase) and exceed 10 MB (after the first 10 MB it is limited to 2 MB/s), with an idle timeout of 5 minutes (Regional and private) or 30 seconds (edge-optimized); caching, VTL response transformation and content encoding are unavailable, and requests stay capped at 10 MB. Older material may say API Gateway always buffers the whole response, so the 29 s and 10 MB caps always apply. - Lambda behind API Gateway: synchronous invocation payloads are capped by Lambda at 6 MB (see
notes/lambda-guide.mdandnotes/current-facts.md), below API Gateway's 10 MB. - Header limits: REST 20,480 bytes of headers (private APIs 8,000); HTTP APIs 10,240 bytes for the request line plus headers. WebSocket (§13): 128 KB messages in 32 KB frames.
Developer angle
# Customize a gateway response: a clearer 429 body (redeploy afterwards)
aws apigateway put-gateway-response --rest-api-id a1b2c3d4e5 --response-type THROTTLED \
--response-templates '{"application/json": "{\"message\": \"Too many requests, retry with backoff\", \"requestId\": \"$context.requestId\"}"}'
# Per-integration timeout (above 29000 ms only after the quota increase, Regional or private APIs)
aws apigateway update-integration --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 \
--http-method POST --patch-operations op=replace,path=/timeoutInMillis,value=25000
# Stream a Lambda response (REST): STREAM mode plus the response-streaming invocation URI, then redeploy
aws apigateway update-integration --rest-api-id a1b2c3d4e5 --resource-id k9l8m7 --http-method GET \
--patch-operations op=replace,path=/responseTransferMode,value=STREAM \
'op=replace,path=/uri,value=arn:aws:apigateway:eu-west-1:lambda:path/2021-11-15/functions/arn:aws:lambda:eu-west-1:111122223333:function:report-stream/response-streaming-invocations'
Access log format (JSON) that separates gateway errors from backend errors:
{"requestId": "$context.requestId", "status": "$context.status",
"errorType": "$context.error.responseType", "errorMessage": "$context.error.message",
"integrationStatus": "$context.integration.status", "integrationError": "$context.integration.error",
"integrationLatency": "$context.integrationLatency", "responseLatency": "$context.responseLatency"}
Traps
- Trap: "504, so raise the Lambda timeout." → Reality: API Gateway's integration timeout (29 s REST default, 30 s HTTP) is the ceiling, and the function keeps running after API Gateway stops waiting.
- Trap: "502, so add memory." → Reality: 502 means the function failed or broke the proxy contract: read its error logs and fix the response shape.
- Trap: "403 Missing Authentication Token, so the token is wrong." → Reality: usually the URL hits a path, method or stage that doesn't exist in the deployment, or an IAM method was called unsigned.
- Trap: "Raise the 10 MB payload quota for file uploads." → Reality: it can't be raised; upload straight to S3 with a presigned URL (see
notes/s3-guide.md). - Trap: "Raise the timeout of the edge-optimized API." → Reality: only Regional and private REST APIs can exceed 29 s.
Practice questions
-
A report endpoint on a Regional REST API calls a Lambda function that runs for 40–50 seconds. Clients receive
504after 29 seconds, while the function's logs show it succeeds. The team wants the smallest change that returns the report in the same request. What should the developer do?- A. Increase the Lambda function timeout to 60 seconds and redeploy the API.
- B. Move the API to an edge-optimized endpoint with a longer timeout.
- C. Enable a 300-second API cache on the report method.
- D. Raise the integration timeout after a Service Quotas increase.
-
A browser client uploads files of up to 50 MB through a REST API to a Lambda function and receives
413errors. Which TWO changes are appropriate? (Select TWO.)- A. Request a payload quota increase for the API in Service Quotas.
- B. Have the API return an S3 presigned URL so the client uploads directly to S3.
- C. Enable response streaming on the upload method.
- D. Move to an HTTP API, which accepts larger payloads.
- E. Process each file asynchronously when it lands in S3, for example from an S3 event notification.
Answers
- D. Regional REST APIs can raise the integration timeout above 29 s once the quota is increased. A does not change API Gateway's limit; B moves to the one REST endpoint type that cannot exceed 29 s; C does not make the first, uncached call faster.
- B, E. Bypass API Gateway's 10 MB limit by uploading to S3 and processing the object asynchronously. A is impossible because the 10 MB limit is fixed; C applies to responses only; D has the same 10 MB limit.
12. Logging metrics and tracing
Concept
- Execution logs (REST and WebSocket): API Gateway's own record of each request (authorizer, mapping, integration call, errors), written to a log group it manages for each stage. Levels:
OFF,ERROR(errors only) andINFO(errors and info); data tracing additionally records full request and response payloads, handy in development and risky in production. ⚠️ Changed: the levels areOFF,ERRORandINFO, with data tracing as a separate switch. Older material may say the levels are ERROR, DEBUG and INFO. - Access logs (REST, HTTP and WebSocket): one line per request in a format you build from
$contextvariables (CLF, JSON, XML or CSV), sent to a log group you choose; REST can also deliver them to Amazon Data Firehose. - REST (and WebSocket) logging needs, once per Region, an IAM role that API Gateway can assume (trusting
apigateway.amazonaws.com, with the managed policyAmazonAPIGatewayPushToCloudWatchLogs) set as the account'scloudwatchRoleArn. HTTP API access logs use log delivery instead: the user who enables them needslogs:CreateLogDeliveryand related permissions, no account role. - Metrics (namespace
AWS/ApiGateway, every minute): RESTCount,Latency,IntegrationLatency,4XXError,5XXError,CacheHitCount,CacheMissCount(dimensionApiName); HTTPCount,Latency,IntegrationLatency,4xx,5xx,DataProcessed; WebSocketConnectCount,MessageCount,IntegrationError,ClientError,ExecutionError,IntegrationLatency(both byApiId). Per-method or per-route dimensions require detailed metrics (extra CloudWatch cost). Latencyis the whole time inside API Gateway,IntegrationLatencyincluded; the difference is API Gateway's own work (authorizers, mapping, WAF, cache lookups).- X-Ray active tracing (REST, all endpoint types) is a stage setting: API Gateway samples requests according to sampling rules and passes the trace header on, so with Lambda tracing you see the whole path. Instrumenting your own code (SDKs, OpenTelemetry, agents): see
notes/monitoring-guide.md.
Exam facts
- Execution log group:
API-Gateway-Execution-Logs_{rest-api-id}/{stage_name}; canary requests also log to a/Canarygroup. Standard events are truncated at 1 KB, and AWS recommendsERRORorINFO(not data tracing) in production. Authorization headers and API key values are redacted. - CloudWatch Logs delivery for REST execution logs is an alternative path: events up to 1 MB, sent to your own log groups, S3 buckets or Firehose streams (several at once); it replaces the managed log group for that stage, so update dashboards, alarms and subscription filters first.
- Without the account role in that Region, enabling REST logs fails. Changing stage logging needs no redeploy.
- Access log format: up to 3 KB. Useful fields:
$context.requestId,$context.extendedRequestId,$context.identity.sourceIp,$context.status,$context.responseLatency,$context.integrationLatency,$context.integration.status,$context.integration.error,$context.error.responseType,$context.authorizer.error,$context.waf.error,$context.xrayTraceId. For HTTP APIs,$context.integrationErrorMessageexplains integration failures. 4XXErroralso counts modified gateway responses; theAveragestatistic of4XXError,5XXErrororCacheHitCountis a rate.- HTTP APIs have access logs and metrics only: no execution logs and no X-Ray tracing. The X-Ray SDKs and daemon are in maintenance mode (see
notes/monitoring-guide.mdandnotes/current-facts.md); API Gateway's active tracing is a stage switch and needs no SDK. Who changed an API (UpdateStage,CreateDeployment): CloudTrail.
Developer angle
# Once per Region: the role API Gateway uses to write logs
aws apigateway update-account --patch-operations \
op=replace,path=/cloudwatchRoleArn,value=arn:aws:iam::111122223333:role/apigw-cloudwatch-logs
# Stage logging, metrics, tracing and access logs from a JSON patch file
aws apigateway update-stage --rest-api-id a1b2c3d4e5 --stage-name prod \
--patch-operations file://stage-logging.json
# Alarm when more than 5 % of prod requests fail with 5XX for 5 minutes
aws cloudwatch put-metric-alarm --alarm-name orders-api-5xx-rate --namespace AWS/ApiGateway --metric-name 5XXError \
--dimensions Name=ApiName,Value=orders-api Name=Stage,Value=prod --statistic Average --period 60 \
--evaluation-periods 5 --threshold 0.05 --comparison-operator GreaterThanThreshold
stage-logging.json:
[{"op": "replace", "path": "/*/*/logging/loglevel", "value": "INFO"}, {"op": "replace", "path": "/*/*/logging/dataTrace", "value": "false"},
{"op": "replace", "path": "/*/*/metrics/enabled", "value": "true"}, {"op": "replace", "path": "/tracingEnabled", "value": "true"},
{"op": "replace", "path": "/accessLogSettings/destinationArn", "value": "arn:aws:logs:eu-west-1:111122223333:log-group:orders-api-access"},
{"op": "replace", "path": "/accessLogSettings/format", "value": "{\"requestId\":\"$context.requestId\",\"status\":\"$context.status\",\"latency\":\"$context.responseLatency\"}"}]
Traps
- Trap: "Set the stage to INFO and the logs appear." → Reality: the account-level CloudWatch Logs role must be set in that Region first.
- Trap: "Turn on data tracing in production to debug." → Reality: it records full payloads, sensitive data included; prefer access logs with targeted fields.
- Trap: "High
Latency, so the Lambda function is slow." → Reality: compare it withIntegrationLatency; if that is low, the time is spent inside API Gateway (authorizer, mapping, WAF). - Trap: "Per-method metrics exist by default." → Reality: only API and stage dimensions do; enable detailed metrics.
- Trap: "Enable X-Ray on the HTTP API stage." → Reality: active tracing is a REST feature.
Practice questions
-
Users report that a REST API is slow. CloudWatch shows
Latencyaveraging 900 ms whileIntegrationLatencyaverages 80 ms for the same method. Where should the developer look first?- A. The memory size and code of the backend Lambda function
- B. Work done inside API Gateway, such as the authorizer and mapping templates
- C. The read capacity and indexes of the DynamoDB table that the function queries
- D. The client's network path to the Regional endpoint
-
A developer turns on execution logging for a REST API stage, but no log group appears and the console reports a problem with the CloudWatch Logs role. Which TWO actions are needed? (Select TWO.)
- A. Create an IAM role that
apigateway.amazonaws.comcan assume, with theAmazonAPIGatewayPushToCloudWatchLogsmanaged policy. - B. Add
logs:CreateLogGroupto the backend Lambda function's execution role. - C. Redeploy the API to the stage after enabling logging.
- D. Set that role as
cloudwatchRoleArnin the API Gateway account settings for the Region. - E. Enable detailed CloudWatch metrics on the stage.
- A. Create an IAM role that
Answers
- B. About 820 ms is spent inside API Gateway, outside the integration, so authorizers and mapping are the first suspects. A and C are part of
IntegrationLatency, which is low; D is outside both metrics, which measure from when API Gateway receives the request. - A, D. API Gateway writes logs through an account-level role per Region. B concerns the function's own logs; C is unnecessary because stage settings apply without a redeploy; E adds metrics, not logs.
13. WebSocket APIs
Concept
- A WebSocket API keeps one open connection per client (
wss://{api-id}.execute-api.{region}.amazonaws.com/{stage}), and either side can send at any time: chat, live dashboards, notifications, multiplayer games, trading screens. - Routes:
$connectruns while the connection is being set up (the place to authorize and to store the connection ID);$disconnectruns after it closes, on a best-effort basis;$defaulttakes messages that match no route or aren't JSON; custom routes (join,sendMessage) are chosen by the API's route selection expression, usually$request.body.action. - Each connection has a
connectionId. The backend stores it (commonly in DynamoDB) and later pushes data through the@connectionsAPI:POSTsends a message,GETreturns the connection's status,DELETEdisconnects the client. These requests go tohttps://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connectionId}and must be SigV4-signed by a principal allowedexecute-api:ManageConnections. Integrations can be Lambda (proxy or custom), HTTP endpoints or AWS services; add a route response when an integration's reply should go straight back to the sender.
Exam facts
- ⚠️ Changed: the
@connectionscallback URL is anhttps://endpoint called with SigV4 (API Gateway Management API). Older material may say it is awss://URL. - Authorization exists only on
$connect(IAM or a LambdaREQUESTauthorizer, optionally requiring an API key) and covers the whole connection; a failure returns 401 or 403 and no connection is made. $disconnectdelivery is not guaranteed: also remove stale IDs when@connectionsraisesGoneException(raised after the client left, or when posting before$connecthas finished), or expire them with a DynamoDB TTL.- Quotas: 500 new connections per second per account per Region (adjustable); 2-hour maximum connection duration and 10-minute idle timeout (both fixed); 128 KB message payload in frames of up to 32 KB (a larger frame closes the connection with code
1009); 29 s integration timeout; 300 routes per API; 8 KB Lambda authorizer result. There is no quota on concurrent connections. - IAM resource ARNs: routes
arn:aws:execute-api:{region}:{account-id}:{api-id}/{stage}/{route-key}; callbacksarn:aws:execute-api:{region}:{account-id}:{api-id}/{stage}/POST/@connections/*. - Lambda proxy events carry
requestContext.connectionId,requestContext.routeKey,requestContext.domainNameandrequestContext.stage; build the callback endpoint from the domain name and stage (drop the stage when a custom domain is used). Metrics:ConnectCount,MessageCount,IntegrationError,ClientError,ExecutionError(§12).
Developer angle
aws apigatewayv2 create-api --name chat-ws --protocol-type WEBSOCKET --route-selection-expression '$request.body.action'
aws apigatewayv2 create-route --api-id w1s2k3 --route-key sendMessage --target integrations/i4n5t6
Broadcast handler: reads connection IDs from DynamoDB and deletes the ones that are gone.
import json, os
import boto3
table = boto3.resource("dynamodb").Table(os.environ["CONNECTIONS_TABLE"])
def handler(event, context):
ctx = event["requestContext"]
client = boto3.client("apigatewaymanagementapi", endpoint_url=f"https://{ctx['domainName']}/{ctx['stage']}")
text = json.loads(event.get("body") or "{}").get("text", "")
for item in table.scan(ProjectionExpression="connectionId")["Items"]:
try:
client.post_to_connection(ConnectionId=item["connectionId"], Data=text.encode("utf-8"))
except client.exceptions.GoneException:
table.delete_item(Key={"connectionId": item["connectionId"]})
return {"statusCode": 200}
Execution-role statement that allows the callbacks:
{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": "execute-api:ManageConnections",
"Resource": "arn:aws:execute-api:eu-west-1:111122223333:w1s2k3/prod/POST/@connections/*"}]}
Traps
- Trap: "Attach an authorizer to the
sendMessageroute." → Reality: WebSocket authorization runs on$connectonly. - Trap: "
$disconnectreliably deletes connection records." → Reality: best effort; handleGoneExceptionand use TTLs. - Trap: "Push server messages over
wss://…/@connections." → Reality: callbacks are signed HTTPS calls to the API Gateway Management API. - Trap: "Poll a REST API every second for updates." → Reality: WebSocket push removes polling (or AppSync subscriptions, see
notes/other-services-guide.md). - Trap: "Connections stay open indefinitely." → Reality: 2-hour lifetime and 10-minute idle timeout: clients must reconnect and send heartbeats.
Practice questions
-
A chat backend keeps WebSocket connection IDs in DynamoDB. During broadcasts some
PostToConnectioncalls fail withGoneException, and the table keeps growing. What should the developer do?- A. Increase the WebSocket idle timeout to 2 hours.
- B. Retry each failed
PostToConnectioncall with exponential backoff and jitter until it succeeds. - C. Delete the stored connection ID when
PostToConnectionraisesGoneException. - D. Move the authorization logic from
$connectto the custom routes.
-
Clients of a WebSocket API send
{"action": "subscribe", "symbol": "ACME"}, and a Lambda function later pushes price updates to subscribed clients. Which TWO configurations are required? (Select TWO.)- A. Set the route selection expression to
$request.body.actionand create asubscriberoute. - B. Enable API caching on the
subscriberoute. - C. Put the pushing function on a usage plan so it may call clients.
- D. Attach a JWT authorizer to the
subscriberoute. - E. Allow the pushing function's role
execute-api:ManageConnectionsand call the@connectionsendpoint for each connection.
- A. Set the route selection expression to
Answers
- C. A gone connection never comes back, so its record should be deleted. A is impossible because the idle timeout is fixed at 10 minutes; B retries calls that cannot succeed; D does not affect stale connections.
- A, E. The route selection expression sends
subscribemessages to their route, and the function pushes updates through@connectionswithManageConnections. B: WebSocket APIs have no cache; C: usage plans don't grant callback rights; D: JWT authorizers belong to HTTP APIs, and WebSocket authorization runs on$connect.
14. OpenAPI import export and SDK generation
Concept
- API Gateway can be driven by an OpenAPI document instead of console clicks. Paths, methods, parameters, models and responses use standard OpenAPI; API Gateway settings use
x-amazon-apigateway-*extensions (-integration,-authorizer,-request-validators,-gateway-responses,-binary-media-types,-corsfor HTTP APIs, and more). Keeping the document in Git makes the API reviewable and repeatable. - REST APIs import OpenAPI 2.0 (Swagger) or 3.0, in JSON or YAML; HTTP APIs import OpenAPI 3.0 only. An import creates or updates the API definition but does not deploy it; clients see the change after a deployment.
- Export turns a REST API stage back into OpenAPI 2.0 or 3.0 (JSON or YAML), optionally with API Gateway integration extensions or Postman extensions: useful for documentation, migration from REST to HTTP, and client generation.
- SDK generation (REST): API Gateway generates a client SDK for a deployed stage in Java, JavaScript, Java for Android, Objective-C or Swift for iOS, and Ruby. Infrastructure as code can embed the same document:
BodyorBodyS3LocationonAWS::ApiGateway::RestApi,DefinitionBodyorDefinitionUriin SAM (seenotes/sam-guide.mdandnotes/cloudformation-guide.md).
Exam facts
- New REST API from a file:
import-rest-api(choose the endpoint type with--parameters endpointConfigurationTypes=REGIONAL;--fail-on-warningsstops on warnings). Updating an existing REST API:put-rest-api --mode mergeor--mode overwrite(merge is the default). - merge adds or replaces what the file defines and leaves the rest alone; overwrite replaces the whole definition and resets anything absent from the file (security policy, authorizers, other API-level settings) to defaults. Either way, import-to-update keeps the API's endpoint configuration, stages, stage variables and API key references.
- Definition files up to 6 MB. Control-plane rates:
ImportRestApi1 request every 3 s (Regional or private) or every 30 s (edge-optimized);PutRestApi1 per second. - Export:
get-export --export-type oas30(orswagger) with--parameters extensions='integrations'(alsoapigateway,authorizers,postman) and--accepts application/yamlfor YAML. Models must useapplication/jsonor the export fails; useextensions='apigateway'to keep request validator settings. - HTTP APIs:
import-api,reimport-apiandexport-api --specification OAS30 --output-type JSON(orYAML);--stage-nameexports that stage, without it the latest configuration is exported; extensions are included unless you pass--no-include-extensions. Importing an OpenAPI 2.0 file as an HTTP API fails; REST features that HTTP APIs lack (request validation, VTL,AWSintegrations) come back as import info or warnings and are dropped. - SDKs:
get-sdk --sdk-type javascript(alsoandroid,rubyand the other supported platforms) for a deployed stage. HTTP APIs have no SDK generation.
Developer angle
OpenAPI 3.0 definition (YAML) with a Lambda proxy integration, a Cognito authorizer and a body validator:
openapi: "3.0.1"
info: {title: orders-api, version: "1.4.0"}
x-amazon-apigateway-request-validators:
body-only: {validateRequestBody: true, validateRequestParameters: false}
paths:
/orders:
post:
security: [{OrdersUsers: []}]
x-amazon-apigateway-request-validator: body-only
requestBody:
required: true
content: {application/json: {schema: {$ref: "#/components/schemas/Order"}}}
responses: {"200": {description: Order accepted}}
x-amazon-apigateway-integration:
type: aws_proxy
httpMethod: POST
uri: arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:111122223333:function:orders-api/invocations
components:
schemas:
Order: {type: object, required: [sku, quantity], properties: {sku: {type: string}, quantity: {type: integer, minimum: 1}}}
securitySchemes:
OrdersUsers:
{type: apiKey, name: Authorization, in: header, x-amazon-apigateway-authtype: cognito_user_pools,
x-amazon-apigateway-authorizer: {type: cognito_user_pools, providerARNs: ["arn:aws:cognito-idp:eu-west-1:111122223333:userpool/eu-west-1_AbCdEf123"]}}
# Create the API from a file, later merge an updated file, then deploy
aws apigateway import-rest-api --parameters endpointConfigurationTypes=REGIONAL --fail-on-warnings --body fileb://orders-api.yaml
aws apigateway put-rest-api --rest-api-id a1b2c3d4e5 --mode merge --body fileb://orders-api.yaml
aws apigateway create-deployment --rest-api-id a1b2c3d4e5 --stage-name prod
# Export the prod stage as OpenAPI 3.0 YAML with integration extensions, and generate a JavaScript SDK
aws apigateway get-export --rest-api-id a1b2c3d4e5 --stage-name prod --export-type oas30 \
--parameters extensions='integrations' --accepts application/yaml orders-api-prod.yaml
aws apigateway get-sdk --rest-api-id a1b2c3d4e5 --stage-name prod --sdk-type javascript orders-api-js-sdk.zip
# HTTP API: import a definition, export a stage
aws apigatewayv2 import-api --body file://orders-http.yaml
aws apigatewayv2 export-api --api-id x1y2z3 --specification OAS30 --output-type YAML --stage-name prod orders-http-prod.yaml
Traps
- Trap: "The new definition was imported, so clients see it." → Reality: an import doesn't deploy; create a deployment.
- Trap: "Use
overwriteto apply one team's partial file." → Reality: overwrite removes everything the file doesn't define; partial files need merge. - Trap: "Import a Swagger 2.0 file as an HTTP API." → Reality: HTTP APIs need OpenAPI 3.0.
- Trap: "Export without extensions to rebuild the API elsewhere." → Reality: the integrations and validators are then missing from the file.
- Trap: "Generate an SDK for the HTTP API." → Reality: SDK generation is a REST API feature.
Practice questions
-
Two teams maintain separate OpenAPI files for different paths of one REST API and release on different days. A developer must apply team B's file without removing the paths defined only in team A's file. Which command should the developer run before deploying?
- A.
aws apigateway put-rest-api --mode mergewith team B's file - B.
aws apigateway put-rest-api --mode overwritewith team B's file - C.
aws apigateway import-rest-apiwith team B's file - D.
aws apigatewayv2 reimport-apiwith team B's file
- A.
-
A company wants to keep an existing REST API's definition in source control and recreate the API, with all its Lambda integrations, in another account. Which TWO steps support this? (Select TWO.)
- A. Generate a JavaScript SDK with
get-sdkand commit it. - B. Export the stage with
get-exportusing theintegrations(orapigateway) extensions. - C. Export the stage with
get-exportwithout extensions so the file stays portable. - D. Import the file in the target account with
import-rest-api, adjust the account-specific ARNs and create a deployment. - E. Copy the stage's deployment ID into a stage in the target account.
- A. Generate a JavaScript SDK with
Answers
- A. Merge applies team B's definitions and leaves team A's paths in place. B deletes what the file does not define; C creates a separate API; D is the HTTP API command.
- B, D. Exporting with extensions keeps the integrations, and importing plus deploying recreates the API. A produces a client library, not the API definition; C drops the integrations; E fails because deployment IDs belong to one API.
Quick decision rules
API types, endpoints and integrations (§1–§4)
- If you see API keys, usage plans, caching, request validation, WAF, a private endpoint or a canary in the requirements, think REST API.
- If you see "cheapest" or "lowest latency" for Lambda or HTTP backends with JWT/OIDC auth and none of the REST features that HTTP APIs lack, think HTTP API.
- If you see server push, chat or live updates over a persistent connection, think WebSocket API.
- If you see a custom domain for an edge-optimized API, think ACM certificate in us-east-1; for a Regional API, a certificate in the API's Region.
- If you see "callable only from inside the VPC", think private REST API + interface VPC endpoint + resource policy with
aws:SourceVpce. - If you see global clients plus control over caching or edge security, think Regional API behind your own CloudFront distribution.
- If you see a Lambda proxy integration returning 502, think a function error or malformed output:
statusCodeplus a stringbody. - If you see 500 right after a CLI or CloudFormation deployment of a Lambda integration, think missing
lambda:InvokeFunctionresource-based permission forapigateway.amazonaws.com. - If you see API Gateway writing to SQS, Kinesis, EventBridge or Step Functions with no business logic, think AWS service integration (REST
AWStype, or HTTP API first-class integration) with an IAM role. - If you see "invoke the function asynchronously from the API", think Lambda custom integration with
X-Amz-Invocation-Type: 'Event'. - If you see JSON clients and an XML/SOAP backend without extra compute, think REST custom integration with VTL mapping templates.
- If you see "reject bad requests before the backend", think request validator plus model (400).
Deployment, caching and throttling (§5–§7, §14)
- If you see "changes don't show up", think redeploy the API to the stage.
- If you see one API definition calling different Lambda versions per environment, think stage variables pointing at Lambda aliases (plus a permission per alias).
- If you see "send a small percentage of stage traffic to a new API version and compare", think canary release on the stage, then promote.
- If you see infrastructure as code for the API definition, think OpenAPI with
x-amazon-apigateway-integration; partial updates useput-rest-api --mode merge. - If you see "client library for mobile or web callers", think
get-sdkon a deployed REST stage. - If you see repeated identical GETs overloading a backend, think stage cache with a suitable TTL (REST).
- If you see users receiving each other's cached data, think missing cache key parameter.
- If you see clients bypassing the cache with
Cache-Control: max-age=0, think Require authorization plusexecute-api:InvalidateCache. - If you see per-customer tiers, quotas or metering, think usage plans + API keys.
- If you see 429 across several APIs in one account, think shared account throttle: stage or method limits and a Service Quotas increase.
- If you see callers with AWS credentials (roles, other accounts), think
AWS_IAMauthorization with SigV4. - If you see cross-account IAM callers getting 403, think both the resource policy and the caller's identity policy must allow.
- If you see your own user pool and no code, think Cognito user pool authorizer (REST) or JWT authorizer (HTTP).
- If you see third-party tokens or custom rules combining several request values, think Lambda
REQUESTauthorizer with caching. - If you see API keys offered as the authentication mechanism, think wrong: keys identify plans, authorizers authenticate.
- If you see a browser CORS error with a proxy integration, think the backend must return
Access-Control-Allow-Origin; for API Gateway's own errors, add it toDEFAULT_4XX/DEFAULT_5XX.
Errors, limits and monitoring (§11–§13)
- If you see 504 after about 29 seconds, think integration timeout: go asynchronous, stream, or raise it on a Regional/private REST API.
- If you see 413 on uploads, think 10 MB limit: S3 presigned URL.
- If you see 403 "Missing Authentication Token", think wrong path, method or stage (or an unsigned IAM call).
- If you see high
Latencywith lowIntegrationLatency, think time spent in API Gateway (authorizer, mapping, WAF). - If you see execution logs that won't turn on, think account-level
cloudwatchRoleArnin that Region. - If you see a backend that must message WebSocket clients, think
@connectionsPOST withexecute-api:ManageConnections; onGoneException, delete the connection ID.
Changed vs older material
Learn the Current column; older practice material may still show the Course / older material value. After you confirm them, the same rows go into notes/current-facts.md with their verification dates.
| Topic | Course / older material | Current (verified 2026-10-12) | Source |
|---|---|---|---|
| §11 · REST integration timeout | 29 s maximum, hard limit for every API | REST 50 ms–29 s by default; Regional and private REST APIs can exceed 29 s through Service Quotas (L-E5AE38E3, since 2024-06-04), possibly lowering the account throttle quota; edge-optimized stays at 29 s; HTTP APIs 30 s fixed; WebSocket 29 s |
AWS docs |
| §11 · REST response streaming | Not available: the full response is always buffered, so the 29 s and 10 MB caps always apply | Since 2025-11-19 on all REST endpoint types: responseTransferMode STREAM (default BUFFERED) on HTTP_PROXY/AWS_PROXY; up to 15 min; over 10 MB (2 MB/s after the first 10 MB); idle timeout 5 min (Regional/private) or 30 s (edge); no caching, VTL response transformation or content encoding; requests stay capped at 10 MB |
AWS docs |
| §7 · Account-level throttle | 10,000 requests per second across all APIs (soft limit) | 10,000 rps with a 5,000 burst per account per Region, shared by REST, HTTP and WebSocket APIs; 13 newer Regions default to 2,500 rps with a 1,250 burst; the rate is adjustable (L-8A5B8E43), the burst is set by AWS |
AWS docs |
| §12 · Execution log levels | ERROR, DEBUG, INFO | OFF, ERROR, INFO, plus a separate data-tracing switch for full payloads |
AWS docs |
| §5 · Stage variables reaching Lambda | Passed to the Lambda "context" object | In the request event: event['stageVariables'] with Lambda proxy; via the mapping template ($stageVariables) with a custom integration |
AWS docs |
| §13 · WebSocket callback URL | wss://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connectionId} |
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connectionId}, SigV4-signed, execute-api:ManageConnections |
AWS docs |
| §4 · HTTP API data mapping | HTTP APIs have no data mapping | Parameter mapping: request headers and query strings (append, overwrite, remove), the path (overwrite), response headers and the status code; no body transformation (VTL) and no request validation | AWS docs |
| §10 · Where the CORS headers go | The OPTIONS preflight request must contain Access-Control-Allow-Methods, -Headers and -Origin |
They are returned in the response to the preflight (the OPTIONS method's 200 response); the browser's preflight carries Origin and Access-Control-Request-Method |
AWS docs |
| §3 · REST private integrations | A REST API reaches private resources only through an NLB (VPC link) | VPC link V2 to an Application Load Balancer or a Network Load Balancer (HTTP_PROXY, connectionType VPC_LINK); VPC link V1 integrations can be switched to V2 |
AWS docs |
| §9 · Lambda authorizer output | Every Lambda authorizer returns an IAM policy | REST: principalId + IAM policy; HTTP API payload 2.0 with simple responses: {"isAuthorized": true} or false |
AWS docs |