
Amazon API GatewayAuthorCloudBrum editorial teamLast reviewedReading time~12 minLevelBeginner to advanced
Amazon Web Services · App modernization · Managed API front door
One front door for your APIs. Clients call a single managed address. API Gateway checks who they are and turns away the ones asking too often. Everyone else is passed to Lambda, a container service or any HTTP back end. Pick an HTTP API when you want it cheap and simple. Pick a REST API when you need caching or a web application firewall.
On this page (17 sections)
Mind map: Amazon API Gateway at a glance
Free to share. The downloaded image includes the CloudBrum link.
Key features (summary)
- Three kinds of APIREST APIs have the most features, HTTP APIs are leaner and cheaper, and WebSocket APIs keep a two-way connection open for chat or live dashboards.
- Many ways to check a callerIAM signatures, Amazon Cognito, JWT tokens from any OpenID Connect provider, Lambda authorizers you write yourself, and mutual TLS on a custom domain.
- Throttling out of the boxA token-bucket limit applies per Region for the account, and you can tighten it for a stage, a route or (on REST APIs) each customer through a usage plan.
- Reaches private back endsA VPC link lets the gateway call services that have no public address, such as an internal load balancer.
- Your own domainAttach a custom domain with an ACM certificate, map base paths to different APIs, and on REST APIs route by header or path with routing rules.
- Start from an OpenAPI fileImport an OpenAPI definition to create routes and models, and export one again when another tool needs it.
- Streaming responsesREST APIs with a proxy integration can stream the response as it is produced, which gets around the usual timeout and size limits for long outputs.
- Developer portalA managed portal can list your REST APIs and generate their documentation, so partners can find what they are allowed to call.
Basic concepts
- API
- The set of HTTP routes you publish. In API Gateway it is also the top-level object you create, with its own ID and invoke URL.
- Route and method
- HTTP and WebSocket APIs use routes such as GET /orders/{id}. REST APIs build the same thing from resources and methods.
- Integration
- What happens when a route is called: run a Lambda function, forward to an HTTP address, call an AWS service, or return a fixed answer.
- Proxy integration
- API Gateway passes the whole request to the back end as it is and returns whatever the back end sends, without reshaping it.
- Stage
- A named, deployed copy of an API, such as dev or prod. HTTP APIs also offer a $default stage that is served from the root of the URL.
- Deployment
- A snapshot of a REST API’s configuration. Changes only go live after you deploy to a stage, which catches many people out.
- Endpoint type
- Edge-optimised (through CloudFront), Regional (served from one Region) or private (reachable only from inside your VPC, REST APIs only).
- Authorizer
- The check that runs before the integration. It can be IAM, a Cognito user pool, a JWT validator or a Lambda function that returns an allow or deny policy.
- Usage plan and API key
- A REST API feature that gives each client its own rate and quota. A key identifies the client for metering. It is not a password.
- Throttling
- A token-bucket limit on requests per second with a burst allowance. Over the limit, callers get HTTP 429.
- Mapping template
- A Velocity template on a REST API that reshapes a request or response, for example to call DynamoDB or SQS without a Lambda function in between.
- VPC link
- A private connection from the gateway to a load balancer or service in your VPC.
- Custom domain name
- Your own hostname in front of one or more APIs, with a TLS certificate from AWS Certificate Manager and base path mappings.
- Payload format version
- The shape of the event sent to Lambda. HTTP APIs default to version 2.0, which is flatter and smaller than the REST format.
How to create & configure it
Create an HTTP API in the AWS console
- Open API Gateway and pick HTTP APISearch for API Gateway, choose your Region and press Build under HTTP API. If you already know you need keys or caching, pick REST API instead.
- Add an integrationChoose Lambda and select the function. For an existing web service pick HTTP URL and paste its address.
- Define the routesAdd the method and path you want, for example GET /orders. A catch-all route such as ANY /{proxy+} is handy early on and too loose for production.
- Accept the default stageThe $default stage with auto-deploy means every change is live at once. Create named stages later if you want separate dev and prod copies.
- Call itCopy the invoke URL and try it with curl. A 200 with your function’s output means the whole chain works. A 5xx usually points at the integration.
- Add authorisationUnder Authorization create a JWT authorizer with your identity provider’s issuer URL and the app’s client ID, then attach it to the routes that need it.
- Set CORS if a browser calls itAllow only your site’s origin and the methods and headers you use. Leaving it at * is fine for a demo and a mistake for anything real.
- Limit the damageSet default route throttling on the stage, and decide what the right ceiling is before a bug or a bad client decides for you.
- Switch on logs and add a domainTurn on access logging to a CloudWatch log group, add an alarm on 5xx and latency, then attach your custom domain name.
Tip: once it works by hand, describe the API as code (OpenAPI with AWS SAM, the CDK or Terraform, see the code references) so dev and prod are built the same way and a mistake is easy to undo.
Common issues after configuration
| Symptom | Likely cause | What to check / fix |
|---|---|---|
| 403 Missing Authentication Token | Wrong URL, path, method or stage | This message is API Gateway’s odd way of saying the route doesn’t exist. Compare the path and stage in the URL with what you created, and check the HTTP method. |
| 502 Bad Gateway with Malformed Lambda proxy response | The function returned the wrong shape | A proxy integration needs statusCode and a string body. Return a JSON string, not an object, and check the payload format version. |
| 504 Gateway Timeout or Endpoint request timed out | The back end took longer than the integration timeout | The default is 29 seconds on REST APIs and 30 on HTTP APIs. Speed the back end up, hand slow work to a queue, or look at response streaming. |
| 429 Too Many Requests | A throttle was hit | Find out which one: the account limit, the stage or route limit, or a usage plan. Ask callers to back off, and raise the limit that is actually in the way. |
| 403 Forbidden after adding an authorizer | Token missing, expired or rejected | Check the header name, the issuer and audience for a JWT authorizer, or the policy a Lambda authorizer returns. Remember authorizer results can be cached. |
| Browser blocks the call with a CORS error | Missing Access-Control-Allow-* headers | Turn on CORS on the API, and with a proxy integration make sure the function sets the headers on its own responses too. |
| Invalid permissions on Lambda function | No permission for API Gateway to invoke the function | Add a resource-based policy on the function that allows apigateway.amazonaws.com for this API’s ARN. The console does it for you, Terraform and the CLI don’t. |
| Change doesn’t show up (REST API) | Not deployed to the stage | Create a new deployment and pick the stage. HTTP APIs with auto-deploy skip this step. |
| Old data keeps coming back (REST API) | Response cache still holds it | Lower the TTL, invalidate the cache for a request with a Cache-Control header (if you allow it), or flush the stage cache. |
| 413 Request Entity Too Large | Payload is over 10 MB | That limit can’t be raised. Have clients upload large files straight to S3 with a presigned URL and send only the reference. |
| Custom domain gives certificate or 403 errors | Certificate in the wrong Region or no mapping | Edge-optimised domains need the certificate in us-east-1, Regional ones in the API’s Region. Check the base path mapping and the DNS record. |
| Private API cannot be reached | VPC endpoint, DNS or policy | Check the interface endpoint for execute-api, private DNS, the endpoint’s security groups and the API’s resource policy. |
Use cases: when and where to use it
Good fit
- A public API for a mobile or web app with Lambda behind it
- Giving partners limited, metered access to an internal service
- Putting one domain and one set of rules in front of many small services
- Turning an old HTTP service into something that has authentication and rate limits
- Real-time features such as chat or live status over WebSockets
- Webhooks that have to be checked and queued before anything else runs
Consider something else
- One Lambda function and no other needs → a Lambda function URL is simpler and free
- Containers behind a plain load balancer → an Application Load Balancer, often cheaper at high volume
- GraphQL with subscriptions → AWS AppSync
- Calls between services inside one VPC → an internal load balancer or Amazon VPC Lattice
- Full API management with a billing and partner programme → compare Azure API Management or Apigee
Pros: why consider it?
Why teams choose it
- Nothing to patch and nothing to restart
- Scales without planning, and costs nothing when no one calls it
- Fits neatly with Lambda and Cognito, and with WAF on REST APIs
- Several ways to authorise callers, from IAM to plain JWT
- HTTP APIs are cheap for what they do
- Limits, logs and custom domains are settings you change, not code you write
Trade-offs to weigh
- REST and HTTP APIs differ more than the names suggest
- Hard limits you cannot raise: a 10 MB payload and a 30-second ceiling on HTTP APIs
- REST API changes need an explicit deployment
- Mapping templates are awkward to write and test
- Per-call pricing adds up on chatty, high-volume APIs
- The routes and authorizers you build only make sense on AWS
Common threats, vulnerabilities & risks
| Risk | Severity | Why it matters | Mitigation |
|---|---|---|---|
| Route with no authorisation | High | A public route with no authorizer lets anyone run your back end and your bill. | Require an authorizer on every route by default and review any route marked as open. |
| API key treated as a password | High | Keys travel in a header and often end up in app code, so they identify clients but don’t prove who is calling. | Use keys for metering only, and put a real authorizer in front. |
| Over-broad CORS | Medium | * lets any website call the API from a visitor’s browser with their credentials. | List the exact origins, methods and headers you need. |
| Weak input checking | High | The gateway forwards what it gets. Bad input reaches the function unless something stops it. | Use request validation on REST APIs and validate again in the back end. |
| Wide-open resource policy or private API | Medium | A policy that allows too many accounts or addresses defeats the point of a private API. | Name accounts, VPC endpoints and source ranges explicitly. |
| Leaked secrets in logs | Medium | Full request logging can write tokens and personal data into CloudWatch. | Log fields you choose, mask sensitive ones and set a short retention. |
| No throttling plan | Medium | One noisy client or a retry loop can use up the account’s shared limit and slow every other API in the Region. | Set stage and route limits and use usage plans for named clients. |
| Stale certificates and TLS settings | Low–Med | An expired certificate takes the domain down, and an old TLS policy keeps weak protocols alive. | Use ACM with automatic renewal and a current security policy. |
| No monitoring | Low–Med | Attacks and errors show up first as a surprise bill or a support ticket. | Alarm on 4xx, 5xx, latency and call count, and keep access logs. |
Performance optimisation
- Pick HTTP API unless a REST-only feature is truly needed. It also tends to respond a little faster, as well as costing less.
- Use payload format 2.0 with Lambda. The event is smaller and has less to parse.
- Cache what doesn’t change. REST APIs can cache responses per stage, and a short TTL is still a big win for popular GET routes.
- Put CloudFront in front when callers are spread around the world, or when responses can be cached closer to them.
- Keep authorizer work small, and cache its result. A Lambda authorizer that runs on every call adds its own Lambda bill and its own latency.
- Compare the Latency and IntegrationLatency metrics. The gap between them is the gateway’s own share, and usually the smaller one.
- Don’t make callers wait on slow jobs. Take the request, queue it, answer 202, and let the client poll or get a callback.
- Skip mapping templates for large bodies, and avoid extra hops such as a Lambda that only forwards the request to another service.
Reduce monthly cost
FinOps is the habit of managing cloud spend together with engineering. These are the FinOps checks that matter most for Amazon API Gateway.
- Start with an HTTP API. At the first pricing tier it costs roughly a third of a REST API per million calls.
- Don’t pay for features you don’t use. REST API caching is billed by the hour for as long as the cache exists, used or not.
- A cache hit never touches the back end, which is the whole point. Even a one-minute TTL on a hot route can take a visible bite out of Lambda and database charges.
- Watch the neighbours: Lambda, NAT gateways, CloudWatch Logs ingestion and data transfer often cost more than the gateway itself.
- Log only what you need. Full execution logging on a busy API fills CloudWatch quickly, so use access logs, a short retention and sampling.
- Reject junk early with request validation, WAF rules and throttling, so bad calls don’t reach (or bill) the back end.
- Move big downloads and uploads to S3 and CloudFront, not through the API.
- Delete test APIs and old stages. Release custom domains you no longer use.
- Tag APIs by team and product, and set a budget alert on API Gateway as a line item.
- Check the free allowance before a pilot. The first pricing tier isn’t what you’ll pay at scale, so read the page for your Region.
Prices and free-tier amounts in this guide are examples from the public pricing page and differ by Region and over time. Confirm the current numbers before you decide (see Pricing).
Security optimisation
- Authorise every route. Prefer a JWT or Cognito authorizer for end users and IAM for AWS-to-AWS calls.
- Use API keys only to meter and throttle clients, never as the only protection.
- Attach AWS WAF to REST API stages to block common attacks and bad IP ranges. HTTP APIs can’t take a web ACL directly.
- Validate requests (schema, size, required fields) before they reach the function.
- Restrict CORS to the origins you own.
- Use private APIs with VPC endpoints and a resource policy for internal use, and mutual TLS on the custom domain when clients must present a certificate.
- Keep back ends private. Use VPC links and security groups so the only way in is through the gateway.
- Pick a current TLS security policy on custom domains and let ACM renew certificates.
- Send access logs to CloudWatch, record changes in CloudTrail, and keep tokens and personal data out of logs.
- Review the routes and authorizers on every release; unreviewed open routes are the usual cause of an incident.
Governance & compliance
Guardrails
- Required tags: Owner, Environment, CostCenter
- Service control policies that limit who may create public APIs or change authorizers
- A shared pattern for stages, logging and authorizers (a SAM or CDK template)
- A review step before any route is published without an authorizer
Continuous checks (AWS Config)
- “api-gw-associated-with-waf”
- “api-gw-execution-logging-enabled”
- “api-gwv2-access-logs-enabled”
- “api-gw-ssl-enabled” for back-end certificates
- “api-gwv2-authorization-type-configured”
Use AWS CloudTrail to see who changed an API, AWS Config for configuration history and rules, and AWS Artifact for compliance reports. API Gateway is in scope for many assurance programmes, but responsibility is shared: AWS runs the service, and you own the routes, authorisation, data and logs.
Reduce carbon footprint
- Return less. Smaller responses and compressed bodies mean less traffic and less energy for the same job.
- Cache so repeated requests don’t wake the back end at all.
- Reject bad traffic at the edge with WAF and throttling, not after it has used compute.
- Choose a Region close to your users and, where latency and data rules allow, one with a lower-carbon grid.
- Retire APIs and test environments that nobody calls.
- Prefer events over polling, so clients aren’t hammering a route every few seconds.
- The AWS Customer Carbon Footprint Tool shows estimated emissions for your account. Check what it covers for API Gateway before quoting a number.
Pricing
Purchase options
Pay per call Flexible
You pay for each request received, and for data sent out. REST API calls cost more than HTTP API calls, and nothing is charged while nobody calls.
Free allowance Free
New accounts get an allowance of calls for REST, HTTP and WebSocket APIs. The terms have changed over the years, so read the pricing page for what applies to your account.
Cache (REST) Hourly
A REST API cache is billed per hour by size from the moment you switch it on, whether or not anyone hits it.
WebSocket Messages
WebSocket APIs are billed per message and per connection minute, which suits bursts of short chats better than thousands of idle connections.
What you actually pay for
- Calls received (per million), at a lower rate for HTTP APIs and for very high volumes
- Data transferred out, except for private APIs
- REST API cache, by hour and size
- WebSocket messages and connection minutes
- The services around it: Lambda, CloudWatch Logs, WAF, NAT, data transfer and custom-domain certificates handled outside ACM
Rates differ by Region and change over time. As a rough guide, at the first tier in US East an HTTP API costs about $1 per million calls and a REST API about $3.50. Check the official Amazon API Gateway pricing page and the AWS Pricing Calculator for current figures.
Important code references
An HTTP API that sends two routes to one Lambda function and only lets callers with a valid JWT through. The CLI and Terraform tabs build it, with a throttle and access logs. The Python and C# tabs hold the handler: they read the caller from the token claims and answer with JSON. Replace the placeholder names, the account ID and the identity provider details with your own.
# Assumes the function orders-handler already exists, and that you have a Cognito user pool (or another OIDC provider) for the JWT check
REGION=ap-south-1
ACCOUNT=123456789012
FUNCTION_ARN=arn:aws:lambda:$REGION:$ACCOUNT:function:orders-handler
# An empty HTTP API
API_ID=$(aws apigatewayv2 create-api --name orders-api --protocol-type HTTP \
--cors-configuration '{"AllowOrigins":["https://app.example.com"],"AllowMethods":["GET","POST"],"AllowHeaders":["authorization","content-type"]}' \
--query ApiId --output text)
# Connect it to the function (payload format 2.0 is the one the Python and C# samples expect)
INTEGRATION_ID=$(aws apigatewayv2 create-integration --api-id $API_ID \
--integration-type AWS_PROXY --integration-uri $FUNCTION_ARN \
--payload-format-version 2.0 --query IntegrationId --output text)
# Check the JWT in the Authorization header before anything reaches the function
AUTH_ID=$(aws apigatewayv2 create-authorizer --api-id $API_ID --name orders-jwt --authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration Audience=your-app-client-id,Issuer=https://cognito-idp.$REGION.amazonaws.com/your-user-pool-id \
--query AuthorizerId --output text)
# Two routes, both protected
for ROUTE in "GET /orders" "POST /orders"; do
aws apigatewayv2 create-route --api-id $API_ID --route-key "$ROUTE" \
--target integrations/$INTEGRATION_ID --authorization-type JWT --authorizer-id $AUTH_ID
done
# Let API Gateway invoke the function, and only this API
aws lambda add-permission --function-name orders-handler --statement-id orders-api \
--action lambda:InvokeFunction --principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:$REGION:$ACCOUNT:$API_ID/*/*"
# Publish to the default stage with a throttle and access logs
aws apigatewayv2 create-stage --api-id $API_ID --stage-name '$default' --auto-deploy \
--default-route-settings ThrottlingRateLimit=50,ThrottlingBurstLimit=100 \
--access-log-settings DestinationArn=arn:aws:logs:$REGION:$ACCOUNT:log-group:/aws/apigw/orders-api,Format='$context.requestId $context.identity.sourceIp $context.routeKey $context.status $context.responseLatency'
echo "https://$API_ID.execute-api.$REGION.amazonaws.com/orders"
# Assumes aws_lambda_function.orders exists, plus var.region, var.user_pool_id and var.app_client_id for the JWT check
resource "aws_cloudwatch_log_group" "api" {
name = "/aws/apigw/orders-api"
retention_in_days = 30
}
resource "aws_apigatewayv2_api" "orders" {
name = "orders-api"
protocol_type = "HTTP"
cors_configuration {
allow_origins = ["https://app.example.com"]
allow_methods = ["GET", "POST"]
allow_headers = ["authorization", "content-type"]
}
}
resource "aws_apigatewayv2_integration" "orders" {
api_id = aws_apigatewayv2_api.orders.id
integration_type = "AWS_PROXY"
integration_method = "POST"
integration_uri = aws_lambda_function.orders.invoke_arn
payload_format_version = "2.0"
}
resource "aws_apigatewayv2_authorizer" "jwt" {
api_id = aws_apigatewayv2_api.orders.id
name = "orders-jwt"
authorizer_type = "JWT"
identity_sources = ["$request.header.Authorization"]
jwt_configuration {
audience = [var.app_client_id]
issuer = "https://cognito-idp.${var.region}.amazonaws.com/${var.user_pool_id}"
}
}
resource "aws_apigatewayv2_route" "orders" {
for_each = toset(["GET /orders", "POST /orders"])
api_id = aws_apigatewayv2_api.orders.id
route_key = each.key
target = "integrations/${aws_apigatewayv2_integration.orders.id}"
authorization_type = "JWT"
authorizer_id = aws_apigatewayv2_authorizer.jwt.id
}
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.orders.id
name = "$default"
auto_deploy = true
default_route_settings {
throttling_rate_limit = 50
throttling_burst_limit = 100
}
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api.arn
format = jsonencode({
requestId = "$context.requestId"
ip = "$context.identity.sourceIp"
route = "$context.routeKey"
status = "$context.status"
latency = "$context.responseLatency"
})
}
}
resource "aws_lambda_permission" "api" {
statement_id = "AllowOrdersApi"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.orders.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.orders.execution_arn}/*/*"
}
output "invoke_url" {
value = aws_apigatewayv2_stage.default.invoke_url
}
# lambda_function.py (payload format 2.0, the default for HTTP APIs)
import base64
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def reply(status, body, request_id):
return {
"statusCode": status,
"headers": {"Content-Type": "application/json", "X-Request-Id": request_id},
"body": json.dumps(body), # a string, not a dict: a proxy integration needs it that way
}
def handler(event, context):
ctx = event["requestContext"]
request_id = ctx["requestId"]
# The JWT authorizer has already checked the token; its claims arrive with the request
claims = ctx.get("authorizer", {}).get("jwt", {}).get("claims", {})
user = claims.get("sub")
if not user:
return reply(401, {"error": "no user in the token"}, request_id)
method = ctx["http"]["method"]
if method == "GET":
params = event.get("queryStringParameters") or {}
try:
limit = min(int(params.get("limit", "20")), 100)
except ValueError:
return reply(400, {"error": "limit must be a number"}, request_id)
return reply(200, {"user": user, "limit": limit, "orders": []}, request_id)
if method == "POST":
raw = event.get("body") or "{}"
if event.get("isBase64Encoded"):
raw = base64.b64decode(raw).decode("utf-8")
try:
order = json.loads(raw)
except json.JSONDecodeError:
return reply(400, {"error": "the body is not valid JSON"}, request_id)
if not isinstance(order, dict) or not order.get("item"):
return reply(400, {"error": "item is required"}, request_id)
logger.info(json.dumps({"requestId": request_id, "user": user, "action": "create_order"}))
return reply(201, {"id": request_id, "item": order["item"]}, request_id)
return reply(405, {"error": "method not allowed"}, request_id)
// Function.cs: a .NET Lambda handler for an HTTP API (payload format 2.0)
// NuGet packages: Amazon.Lambda.Core, Amazon.Lambda.APIGatewayEvents, Amazon.Lambda.Serialization.SystemTextJson
using System.Text;
using System.Text.Json;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace OrdersApi;
public class Function
{
public APIGatewayHttpApiV2ProxyResponse Handler(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
{
var requestId = request.RequestContext.RequestId;
// The JWT authorizer has already checked the token; its claims arrive with the request
string? user = null;
request.RequestContext.Authorizer?.Jwt?.Claims?.TryGetValue("sub", out user);
if (string.IsNullOrEmpty(user))
{
return Reply(401, new { error = "no user in the token" }, requestId);
}
switch (request.RequestContext.Http.Method)
{
case "GET":
var limit = 20;
var query = request.QueryStringParameters;
if (query is not null && query.TryGetValue("limit", out var text) && !int.TryParse(text, out limit))
{
return Reply(400, new { error = "limit must be a number" }, requestId);
}
return Reply(200, new { user, limit = Math.Min(limit, 100), orders = Array.Empty<string>() }, requestId);
case "POST":
var raw = request.Body ?? "{}";
if (request.IsBase64Encoded)
{
raw = Encoding.UTF8.GetString(Convert.FromBase64String(raw));
}
try
{
using var doc = JsonDocument.Parse(raw);
if (!doc.RootElement.TryGetProperty("item", out var item) || item.ValueKind != JsonValueKind.String)
{
return Reply(400, new { error = "item is required" }, requestId);
}
context.Logger.LogInformation(JsonSerializer.Serialize(new { requestId, user, action = "create_order" }));
return Reply(201, new { id = requestId, item = item.GetString() }, requestId);
}
catch (JsonException)
{
return Reply(400, new { error = "the body is not valid JSON" }, requestId);
}
default:
return Reply(405, new { error = "method not allowed" }, requestId);
}
}
private static APIGatewayHttpApiV2ProxyResponse Reply(int status, object body, string requestId) => new()
{
StatusCode = status,
Headers = new Dictionary<string, string> { ["Content-Type"] = "application/json", ["X-Request-Id"] = requestId },
Body = JsonSerializer.Serialize(body)
};
}
Best videos to explore
Short walkthrough videos for this service are being prepared. They will appear here and load only when you press play. Until then, follow CloudBrum on YouTube for new videos.
Interview questions
BasicWhat is Amazon API Gateway?
It is a managed service that sits between your callers and your back end. You define routes, API Gateway handles the HTTPS endpoint, checks who is calling, limits traffic, logs requests and forwards them to Lambda, an HTTP service or an AWS service. You don’t run any servers for it.
BasicWhat is a stage?
A stage is a named, deployed version of an API, such as dev, test or prod, each with its own URL and settings like throttling, logging and variables. On REST APIs nothing is live until you deploy to a stage. HTTP APIs can auto-deploy to a stage called $default.
BasicWhat is the difference between a REST API and an HTTP API?
Both are request and response APIs. REST APIs have more features: API keys and usage plans, request validation, response caching, AWS WAF, private endpoints and mapping templates. HTTP APIs have fewer features, respond faster and cost much less per call, and they add simple JWT authorisation. The usual advice is to start with an HTTP API and move to REST only if you need something it lacks.
IntermediateHow can you secure an API in API Gateway?
Pick the authorisation that fits the caller. IAM for AWS-signed calls, a Cognito or JWT authorizer for end users, and a Lambda authorizer when the rules are your own. Add mutual TLS if clients must hold a certificate, a resource policy to limit who or where calls may come from, and AWS WAF on REST APIs for common attacks. API keys are for metering clients, not for proving identity.
IntermediateHow does throttling work?
Limits use a token bucket, with a steady rate and a burst size. There is an account limit per Region shared by all your APIs, and you can set lower limits per stage or route. On REST APIs, usage plans add a separate rate and quota for each API key. A request over any limit gets HTTP 429, so clients should back off and retry.
IntermediateHow do you reach a back end that has no public address?
Create a VPC link and use a private integration to a load balancer in your VPC. For callers, you can keep the API itself private with a REST API private endpoint and an interface VPC endpoint. In both cases security groups and resource policies, not obscurity, limit who gets in.
AdvancedA request behind API Gateway sometimes needs 40 seconds. What are your options?
First ask whether the caller really has to wait. The usual fix is asynchronous: accept the job, put it on SQS or start a Step Functions workflow, return 202 with an ID, and let the client poll or receive a webhook. If waiting is genuinely needed, a REST API can raise the integration timeout above 29 seconds for Regional APIs (it may lower your account throttle), or use response streaming with a proxy integration. HTTP APIs top out at 30 seconds.
AdvancedHow would you give each customer their own limits and a bill?
On a REST API, give each customer an API key in a usage plan with its own rate and quota, and read call counts from the plan or the logs. Because keys don’t authenticate, still put an authorizer in front and tie the key to the customer. If you need rules the plans can’t express, such as tiered pricing by route, have a Lambda authorizer look up the customer and return a usage identifier, and count calls in your own store.
AdvancedHow would you release a breaking change without hurting existing clients?
Version in the path or the domain and run old and new side by side. Base path mappings or REST routing rules can send /v1 and /v2 to different APIs or stages, and Lambda aliases let a stage point at a specific function version. On REST APIs, a canary release sends a small share of traffic to the new deployment first. Keep the old version until its traffic is gone.
AdvancedUsers report slow responses and occasional 5xx errors. How do you find the cause?
Start with the gateway’s own numbers. Compare Latency with IntegrationLatency: a big gap means the gateway, authorizer or network, and a small gap means the back end. Read access logs for the status, route and integration error, turn on X-Ray, and check 4xx and 5xx counts by route. Then look at Lambda duration and throttles, at cold starts, or at the back end itself. Remember the account throttle is shared, so a noisy neighbour API can cause 429s here.
Similar or alternative services on other clouds
| Capability | AWS | Azure | Google Cloud |
|---|---|---|---|
| Service | Amazon API Gateway | Azure API Management | Apigee and Google Cloud API Gateway |
| Best known for | Cheap HTTP APIs in front of Lambda | Policies and a developer portal for a managed API programme | Apigee: lifecycle management at scale. API Gateway: a small serverless gateway |
| Who is calling | IAM, Cognito, JWT, Lambda authorizer, API keys | Keys, JWT, OAuth 2.0 and client certificates | Apigee: keys and OAuth tokens. API Gateway: keys, JWT and service accounts |
| Slowing greedy callers | Account and stage throttles, plus usage plans on REST APIs | The rate-limit and quota policies | Apigee: SpikeArrest and Quota |
| Limits to know | 10 MB payload, 29 or 30 seconds by default | Depends on the tier, so read the limits page | Depends on the product, so read each limits page |
| Developer portal | A managed portal for REST APIs, added in 2025 | Built in | Apigee: integrated or custom. API Gateway: none |
| Private networks | Private APIs and VPC links | Virtual network options that vary by tier | Apigee: Private Service Connect or VPC peering, and hybrid |
| How it is billed | Per million calls, and the cache by the hour | By the hour per unit, or per call on Consumption | Apigee: subscription or pay-as-you-go. API Gateway: per call |
Not sure a gateway is what you need? A Lambda function URL gives one function its own HTTPS address, an Application Load Balancer fronts containers, and AWS AppSync covers GraphQL.
Did you find this article helpful?
Your answer helps us improve this page for the next reader.
Sources and further reading
CloudBrum wrote this page in its own words, using the provider’s public documentation listed below as the reference. Features, limits, names and prices change, so confirm anything that matters on the provider’s own pages before you decide.
- What is Amazon API Gateway?Amazon Web Services
- Choose between REST APIs and HTTP APIsAmazon Web Services
- Amazon API Gateway quotas and important notesAmazon Web Services
- Quotas for configuring and running an HTTP APIAmazon Web Services
- Throttle requests to your REST APIsAmazon Web Services
- Stream the integration response for proxy integrationsAmazon Web Services
- Amazon API Gateway pricingAmazon Web Services
- Amazon API Gateway adds developer portal capabilities (announcement)Amazon Web Services
- Serverless Applications Lens (Well-Architected Framework)Amazon Web Services
- Terraform AWS provider: aws_apigatewayv2_apiHashiCorp
