Overview & service types

AWS API Gateway comes in three flavours that are not interchangeable:

  • REST API — the original product. Feature-complete: stages, deployment history, per-method throttling, request/response mapping templates (VTL), usage plans with API keys, X-Ray integration. Higher per-call cost. Supports edge-optimised, regional, and private endpoints.
  • HTTP API — launched in 2019, roughly 70% cheaper per call than REST API. Supports JWT authorisers natively, auto-deploy, VPC links to Application Load Balancers and Cloud Map. Missing: usage plans, API keys, per-method throttling, WAF direct attachment (WAF attaches to the CloudFront distribution in front). Best fit for internal microservice backends and workloads where latency cost is critical.
  • WebSocket API — stateful bidirectional connections. Not covered here; distinct architecture requirements.

For a regulated financial-services environment, the choice almost always comes down to REST API with a private or regional endpoint. The feature set — usage plans, per-method throttling, request validation, VTL transforms, native WAF attachment — is what a SAMA security review expects to see at the API boundary. HTTP API is the right choice for lower-sensitivity internal APIs where the missing governance features are provided by IAM policies and the downstream service.

REST API vs HTTP API — the governance gap

HTTP API does not support usage plans or API keys. If your external partners have contractual rate-limit quotas and you need a durable audit record of their quota consumption, you must use REST API or implement quota tracking outside API Gateway (e.g. in a Lambda authoriser writing to DynamoDB). The cheaper call price is real, but the missing governance layer has to be accounted for.

VPC private integrations

The most consequential network-level decision for a bank deployment is whether the API Gateway integrates with backend services through the public internet or through a VPC link. The default is the public internet — your Lambda functions or HTTP endpoints are called from the managed API Gateway fleet over HTTPS. For internal-only backends, this is unacceptable: the data plane traverses AWS infrastructure that is not inside your VPC security boundary.

VPC links solve this. A VPC Link is an elastic network interface injected into your VPC; API Gateway reaches your backend through it without the traffic leaving the AWS private network. There are two VPC link types:

  • VPC Link for REST API — targets a Network Load Balancer inside your VPC. The NLB terminates at your private ALB or directly at your ECS / EKS service target groups.
  • VPC Link for HTTP API — supports ALB, NLB, and AWS Cloud Map service discovery targets directly.
vpc-link-cfn.yamlyaml
# CloudFormation: VPC Link for REST API → internal NLB
Resources:

  PaymentsNLB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: network
      Scheme: internal
      Subnets:
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetB

  ApiGwVpcLink:
    Type: AWS::ApiGateway::VpcLink
    Properties:
      Name: bank-payments-vpclink
      TargetArns:
        - !GetAtt PaymentsNLB.LoadBalancerArn

  PaymentsApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: payments-api
      EndpointConfiguration:
        Types: [ REGIONAL ]     # PRIVATE if this endpoint must not be internet-routable

  PaymentsIntegration:
    Type: AWS::ApiGateway::Method
    Properties:
      Integration:
        Type: HTTP_PROXY
        Uri: !Sub "http://${PaymentsNLB.DNSName}/v1/{proxy}"
        ConnectionType: VPC_LINK
        ConnectionId: !Ref ApiGwVpcLink
        TimeoutInMillis: 29000   # max for REST API; must be < 29s

Two topology decisions embedded in this snippet that matter for a regulated environment:

  • EndpointConfiguration: REGIONAL vs PRIVATE — a private endpoint is accessible only from within the VPC via an Interface VPC Endpoint (PrivateLink). No request reaches it from the internet. This is the correct choice for internal APIs consumed by your core banking middleware. A regional endpoint is internet-accessible but WAF-protected — correct for external partner APIs.
  • TimeoutInMillis: 29000 — API Gateway hard-limits integration timeout at 29 seconds. Any synchronous backend call that takes longer will be cut off with a 504, even if the backend eventually responds. Legacy core banking systems with slow SQL calls regularly exceed this. Design your adapter layer to return a correlation ID and use a polling or callback pattern for long-running operations.

WAF coupling

AWS WAF v2 is the first line of defence for any internet-facing API Gateway endpoint. It evaluates each request before API Gateway processes it — blocking at the WAF level costs nothing in API Gateway invocations. The challenge is configuration: WAF ships with managed rule groups that are opinionated and will generate false positives against financial API payloads if tuned carelessly.

waf-web-acl.yamlyaml
# Terraform: WAF WebACL for API Gateway REST endpoint
resource "aws_wafv2_web_acl" "api_gw_acl" {
  name  = "bank-apigw-waf"
  scope = "REGIONAL"    # CLOUDFRONT for edge-optimised endpoints

  default_action { allow {} }

  rule {
    name     = "AWSManagedRulesCommonRuleSet"
    priority = 1
    override_action { none {} }  # enforce; change to count{} when tuning
    statement {
      managed_rule_group_statement {
        vendor_name = "AWS"
        name        = "AWSManagedRulesCommonRuleSet"
        # SizeRestrictions_BODY blocks payloads > 8KB; ISO 20022 pain.001 exceeds this
        excluded_rule { name = "SizeRestrictions_BODY" }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "CommonRuleSet"
      sampled_requests_enabled   = true
    }
  }

  rule {
    name     = "AWSManagedRulesKnownBadInputsRuleSet"
    priority = 2
    override_action { none {} }
    statement {
      managed_rule_group_statement {
        vendor_name = "AWS"
        name        = "AWSManagedRulesKnownBadInputsRuleSet"
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "KnownBadInputs"
      sampled_requests_enabled   = true
    }
  }

  rule {
    name     = "PartnerIPAllowlist"
    priority = 0             # evaluate before managed rules
    action   { allow {} }    # explicit allow skips remaining rules
    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.partner_cidrs.arn
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "PartnerAllowlist"
      sampled_requests_enabled   = false
    }
  }
}
ISO 20022 payloads and the SizeRestrictions_BODY rule

The AWS Managed Rules CommonRuleSet includes a SizeRestrictions_BODY rule that blocks request bodies larger than 8 KB. ISO 20022 payment initiation messages (pain.001) with multiple transaction lines routinely exceed this. Always exclude this rule and replace it with a custom size constraint set to the maximum your payload specification allows — typically 256 KB for a batch payment file. Do not disable size checks entirely; an unbounded payload is a denial-of-service vector.

WAF tuning is an ongoing activity, not a one-time configuration. The correct procedure for introducing a new managed rule group is:

  1. Set override_action: count — the rule evaluates but does not block.
  2. Run for 7 days under production-representative traffic.
  3. Review sampled blocked requests in the WAF console for false positives.
  4. Add exclusions for legitimate patterns, then switch to override_action: none (enforce).

Partners calling Open Banking endpoints are particularly sensitive to WAF false positives. A blocked request at the WAF level returns a 403 that API Gateway never sees — it will not appear in API Gateway access logs, only in WAF logs. Ensure WAF logs are flowing to CloudWatch Logs or S3 before going live.

Custom authorisers

API Gateway offers three authoriser types: Lambda authoriser (TOKEN or REQUEST), JWT authoriser (HTTP API only), and IAM authoriser. For a financial-services regulated endpoint the choice is almost always a Lambda TOKEN authoriser for REST API, because it gives you full control over the token validation logic — including introspection against a Keycloak or ADFS IdP, FAPI 2.0 certificate-bound token checks, and custom claim mapping to API Gateway context variables.

lambda-authoriser.pypython
import os, json, boto3, hashlib
import urllib.request, urllib.parse

_sm = boto3.client("secretsmanager")
_cache = {}  # in-memory introspection cache (valid for Lambda warm lifecycle only)

def handler(event, context):
    token = event["authorizationToken"].removeprefix("Bearer ")
    method_arn = event["methodArn"]

    # FAPI 2.0: certificate-bound token check (cnf.x5t#S256 claim)
    client_cert_dn = event.get("requestContext", {}).get("identity", {}).get("clientCert", {}).get("subjectDN")

    # Cache key: SHA256 of raw token (never cache the token itself)
    cache_key = hashlib.sha256(token.encode()).hexdigest()[:16]
    if cache_key in _cache:
        return _cache[cache_key]

    claims = _introspect(token)
    if not claims or not claims.get("active"):
        raise Exception("Unauthorized")  # API Gateway interprets this as 401

    # Verify cert binding (RFC 8705 §3.1) when mTLS is enabled on the API
    if client_cert_dn and claims.get("cnf"):
        _verify_cert_binding(client_cert_dn, claims["cnf"])

    policy = _build_policy(claims["sub"], "Allow", method_arn, claims)
    _cache[cache_key] = policy
    return policy

def _build_policy(principal, effect, arn, claims):
    # Wildcard ARN: policy cached by API Gateway is reused across methods in same stage
    arn_parts = arn.split(":")
    arn_wildcard = ":".join(arn_parts[:6]) + ":*/*"
    return {
        "principalId": principal,
        "policyDocument": {
            "Version": "2012-10-17",
            "Statement": [{"Effect": effect, "Action": "execute-api:Invoke", "Resource": arn_wildcard}]
        },
        "context": {
            "sub":   claims.get("sub", ""),
            "scope": claims.get("scope", ""),
            "acr":   claims.get("acr", ""),
        },
    }

The context map returned by the authoriser is available in API Gateway mapping templates and access log format strings as $context.authorizer.sub, $context.authorizer.scope, etc. This is how you pass validated claims downstream to the backend without exposing the raw token.

Authoriser caching and the 5-minute window

API Gateway caches authoriser results by the token value (or the combination of identity sources for REQUEST authorisers) for a configurable TTL — default 300 seconds. This means a revoked token is still valid from API Gateway’s perspective for up to 5 minutes. For payment APIs this is a significant risk window. Either set AuthorizerResultTtlInSeconds to 0 (no caching; every call invokes Lambda) or implement short-lived tokens with a max lifetime of 60 seconds in your IdP. Caching is an important cost optimisation at high volume — weigh the latency and cost saving against the revocation window.

mTLS configuration

AWS API Gateway supports mutual TLS for REST API and HTTP API regional and private endpoints. The configuration is different from Kong: you provide a truststore (a PEM bundle of trusted CA certificates) stored in S3, referenced in the API Gateway configuration. API Gateway evaluates the client certificate against the truststore on every TLS handshake and rejects the connection if the certificate is not signed by a trusted CA.

mtls-config.yamlyaml
# Terraform: mutual TLS on API Gateway custom domain
resource "aws_api_gateway_domain_name" "ob_partner" {
  domain_name              = "api.acme-bank.com"
  regional_certificate_arn = aws_acm_certificate.api_cert.arn
  security_policy          = "TLS_1_2"   # SAMA CSF requires TLS 1.2 minimum

  mutual_tls_authentication {
    # S3 object containing PEM bundle of trusted partner CA certificates
    truststore_uri     = "s3://acme-bank-apigw-config/partner-ca-bundle.pem"
    truststore_version = aws_s3_object.partner_ca_bundle.version_id
  }

  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

# Versioned S3 object for the truststore — rotation = upload new version and update version_id
resource "aws_s3_object" "partner_ca_bundle" {
  bucket = "acme-bank-apigw-config"
  key    = "partner-ca-bundle.pem"
  source = "./pki/partner-ca-bundle.pem"
  etag   = filemd5("./pki/partner-ca-bundle.pem")
}

There are two operational gaps in the AWS mTLS model compared to Kong. First, when a partner certificate is rejected at the TLS layer, the response is a TLS alert, not an HTTP 401 — it is invisible in API Gateway access logs and in the Lambda authoriser. You must enable WAF logging (which captures the TLS handshake failure) or CloudTrail to detect rejected client certificates. Second, there is no built-in certificate expiry monitoring in API Gateway. Build a Lambda function that periodically parses the truststore PEM bundle, extracts NotAfter fields, and publishes a CloudWatch metric that alarms 60 days before expiry.

Throttling & usage plans

API Gateway throttling operates at two levels: the account-level default (10,000 requests/second, 5,000 burst by default in the me-south-1 Bahrain region) and the usage-plan level (per API key, per stage, per method). Usage plans are the mechanism for encoding partner quotas.

  1. Create a usage plan per partner tier

    Create separate usage plans for each commercial tier: standard (1,000 req/day, 10 req/sec), premium (100,000 req/day, 50 req/sec), bulk (1,000,000 req/day, 100 req/sec). Usage plans are not per-partner — they are per tier. Multiple partners share a plan; their individual quota consumption is tracked by their API key.

  2. Generate and issue API keys at partner onboarding

    Create one API key per partner per environment (not one key shared across environments). Associate the key with the appropriate usage plan and the target API stage. Store the key in Secrets Manager and provide it to the partner via a secure channel — never by email.

  3. Configure per-method throttling overrides

    Payment initiation endpoints (POST /payments) deserve lower burst limits than account inquiry (GET /accounts). Use per-method stage-level throttle overrides to enforce this independently of the usage plan limits.

  4. Enable usage plan quota reset and monitor

    Quotas reset at the beginning of each calendar day (UTC) by default. If a partner’s quota resets at midnight Riyadh time (UTC+3), set the quota reset to 21:00 UTC. CloudWatch metric Count filtered by ApiKeyId gives you per-partner request counts for quota audit.

  5. Export usage data for billing and dispute resolution

    The GetUsage API returns per-API-key usage for a date range. Automate a daily export to S3 in Parquet format; this is the durable audit record that answers “how many requests did partner X make on date Y” in a dispute or SAMA audit.

Account-level throttle limits are shared across all APIs

The account-level throttle in API Gateway applies to the entire AWS account in a region. If your account hosts both the Open Banking partner gateway and an internal microservice gateway, a traffic spike on the internal APIs can consume the account-level token bucket and throttle the partner APIs. Request a limit increase from AWS Support for critical regulated accounts, and consider separating regulated-partner APIs into a dedicated AWS account with its own throttle budget.

Request/response transformations

API Gateway REST API uses Apache Velocity Template Language (VTL) for request and response mapping templates. VTL is powerful but idiosyncratic — it is the most common source of subtle bugs in an API Gateway integration. HTTP API uses a simpler payload format version 2.0 with limited transformation support.

The use cases where VTL earns its keep in a bank environment:

  • Extracting a correlation ID from an incoming ISO 20022 message and injecting it as a request header (X-Correlation-ID) for distributed tracing.
  • Mapping a legacy SOAP response from an IBM MQ-backed service into a JSON structure expected by a modern mobile client — avoiding a Lambda proxy layer purely for shape translation.
  • Normalising currency codes: incoming SAR → downstream 682 (ISO 4217 numeric) expected by the core banking system.

VTL templates are part of the API definition, tested in the API Gateway console (Test Invoke) or via the aws apigateway test-invoke-method CLI. The absence of unit tests for VTL is a common gap — treat templates as code, store them in version control, and test them in a dev stage before deploying to production.

Observability & audit logging

API Gateway provides three observability primitives: CloudWatch Metrics (auto-enabled), CloudWatch Logs access logging (opt-in, requires a CloudWatch Logs role ARN on the account), and AWS X-Ray distributed tracing (opt-in per stage).

The access log format is configurable via a JSON template. For SAMA compliance, the minimum fields are:

access-log-format.jsonjson
{
  "requestId":       "$context.requestId",
  "ip":              "$context.identity.sourceIp",
  "caller":          "$context.identity.caller",
  "user":            "$context.identity.user",
  "apiKey":          "$context.identity.apiKey",
  "clientCertDN":    "$context.identity.clientCert.subjectDN",
  "requestTime":     "$context.requestTime",
  "httpMethod":      "$context.httpMethod",
  "resourcePath":    "$context.resourcePath",
  "status":          "$context.status",
  "protocol":        "$context.protocol",
  "responseLatency": "$context.responseLatency",
  "integrationLatency": "$context.integrationLatency",
  "authoriserSub":   "$context.authorizer.sub",
  "authoriserScope": "$context.authorizer.scope",
  "wafResponse":     "$context.wafResponseCode",
  "errorMessage":    "$context.error.message"
}

Forward these logs to your SIEM using a CloudWatch Logs subscription filter pointing to a Kinesis Data Firehose delivery stream, which lands them in S3 (Parquet with Glue catalog) or directly in your SIEM ingest endpoint. The clientCertDN field is only populated when mTLS is enabled on the custom domain; for API-key-authenticated traffic it will be empty. The authoriserSub field relies on the Lambda authoriser returning it in the context map — as shown in the code block above.

Production topology

A production API Gateway topology for a regulated KSA bank uses the Bahrain region (me-south-1) as the primary, with a secondary in UAE (me-central-1) for disaster recovery. Route 53 latency routing or health-check failover handles the DNS tier.

Infrastructure-as-code discipline is non-negotiable here. The API definition, WAF rule set, Lambda authoriser code, VPC link configuration, and CloudWatch log format must all be in Terraform or CloudFormation and deployed through a CI/CD pipeline — not hand-configured in the console. A SAMA audit will ask for change history on the security configuration; a Git-controlled IaC repository provides that history. Console changes do not.

Common pitfalls

The 29-second integration timeout is absolute

API Gateway REST API will terminate any backend integration that does not respond within 29 seconds and return a 504 to the caller. There is no configuration override. Any synchronous API that calls a core banking system with batch-style SQL queries is at risk. The architectural fix is to design these as asynchronous APIs: POST returns 202 with a job ID, GET /jobs/{id} polls for completion. This is also the correct pattern for ISO 20022 payment initiation, which has intrinsically asynchronous settlement semantics.

Lambda cold starts under mTLS scrutiny

If your Lambda authoriser uses provisioned concurrency 0, cold starts add 300–800ms to the first request latency in a warm-up period. For a partner mTLS endpoint where the SLA is 1 second end-to-end, a cold authoriser start is a SLA breach. Use provisioned concurrency for the Lambda authoriser function — the cost is modest relative to the SLA exposure. Alternatively, use API Gateway’s built-in authoriser result cache with a short TTL to absorb the cold-start penalty on subsequent requests.

Private endpoint resource policies are additive, not replace

When you configure a private API Gateway endpoint (accessible only within your VPC), AWS creates an implicit “deny all” policy. You must explicitly create a resource policy that allows access from your VPC Interface Endpoint. A common mistake is applying only an IAM policy and wondering why requests are still blocked. Both the resource policy (which controls network access from the VPC endpoint) and the authoriser (which controls per-caller access) are independently evaluated.

WAF does not attach natively to HTTP API

AWS WAF v2 can be associated directly with a REST API stage, but not directly with an HTTP API. For HTTP API, WAF must be placed on a CloudFront distribution in front of the API. This adds a CloudFront hop, changes the latency profile, and requires managing a CloudFront distribution as part of the API infrastructure. If WAF enforcement is required (and it is for any external API in a SAMA-regulated environment), REST API is the simpler topology choice.

Comparison with Kong & IBM API Connect

AWS API Gateway is a managed service that eliminates the operational overhead of running gateway infrastructure. Kong and IBM API Connect are self-managed (or vendor-SaaS) products that give you more control over the feature set and the deployment topology. Neither model is universally better; the choice depends on which dimension of control matters most.

DimensionAWS API GatewayKong Gateway 3.xIBM API Connect 10.x
Operational overhead Near-zero. AWS manages patching, scaling, HA, and certificate renewal for the gateway itself. Moderate. Team manages Kong pods, upgrades, Redis for rate limiting, and the Postgres or control-plane instance. High. IBM cluster (OCP), DataPower appliances, and gateway services require dedicated operational capacity.
mTLS enforcement Truststore in S3, evaluated at TLS layer on custom domain. No built-in certificate inventory or expiry monitoring. CA certificate registered in Kong, mapped to consumer by DN. Kong Manager shows cert status. DataPower provides the TLS stack. Certificate lifecycle managed via APIM console. Most mature model.
Rate limiting & quotas Usage plans with API keys. Per-method throttling. No sliding-window counters; fixed-window per day. Sliding-window via Redis, per consumer, per route. Highly configurable. Supports burst + daily windows simultaneously. Rate-limit plans tied to product subscriptions. Quota management through API manager portal. Billing-integration ready.
Request transformation VTL mapping templates (REST API). Powerful but operationally fragile. No debugging tooling. Lua/Go plugins. More testable than VTL; plugin PDK has unit test framework. DataPower GatewayScript / XSLT / assembly policies. Industrial-strength; designed for complex message transformations.
Vendor lock-in High. API definitions, authoriser Lambda code, and VTL templates are AWS-specific. Migration cost is real. Moderate. Kong declarative config is portable; plugin dependencies create soft lock-in. Very high. DataPower assemblies and IBM OIDC flows are proprietary. Migration is a multi-year effort.
SAMA / regulated environment fit Good, with configuration discipline. Data residency relies on correct region selection; no guarantee of on-prem data plane. Excellent for cloud-native Kubernetes environments. Data plane fully within your control. Excellent for on-premises or hybrid. Data plane is in your data centre; no cloud dependency required.
TCO at 100M calls/month REST API: ~$350/month in me-south-1. No infrastructure overhead. WAF and Lambda authoriser add ~$100–200. Kong OSS: infrastructure cost (3 pods + Redis). Kong Enterprise: licence plus infrastructure. IBM licence + OCP cluster cost. Typically 5–10× more expensive at equivalent call volume.

Decision framework

The question “should we use AWS API Gateway?” decomposes into four sub-questions that each have a clear answer for most regulated banks:

  • Is the workload on AWS? AWS API Gateway is the lowest-friction choice when your backend services are on ECS, EKS, or Lambda. The VPC Link integration is tight. If your backend is on-prem or on another cloud, the VPC Link requires a VPN or Direct Connect to close the network path, and the latency overhead may be significant.
  • Do you need a developer portal and API catalogue? AWS API Gateway does not include a developer portal. Consumers discover APIs through the developer team, not through a managed catalogue. If you have external third-party developers onboarding to your APIs, you need to build or buy a portal separately (AWS does not have one; consider a custom one via Backstage or a commercial add-on). Kong Enterprise includes a portal; IBM API Connect includes the most mature one in the market.
  • Is vendor lock-in a constraint? SAMA’s IT Governance Framework explicitly calls out cloud provider lock-in as a risk to manage. AWS API Gateway’s VTL templates and authoriser model are not portable. Kong’s declarative config can be migrated to a self-hosted cluster. If your institution’s cloud strategy requires gateway portability, Kong with a cloud-agnostic Kubernetes platform is the lower lock-in option.
  • Who will operate it? AWS API Gateway requires almost no operational staffing for the gateway itself, but it requires significant IaC and security-policy expertise to configure correctly. Kong requires operational staffing for the gateway, but the configuration is more inspectable and debuggable. The right answer depends on the skill profile of the platform team, not on the product feature list.

For a greenfield API programme on AWS — cloud-native backend, small team, no existing gateway investment — AWS API Gateway REST API with WAF and a Lambda authoriser is the correct starting point. Add Kong later if the consumer management and developer portal requirements outgrow what API Gateway provides natively. For a migration from IBM API Connect in a hybrid/on-prem environment, Kong is the closer semantic fit and the lower lock-in choice.