← Blog

Setting Up the Claude API in an AWS Lambda Function

2026-11-07 · 4 min read · SubToAPI Team

Setting up the Claude API inside an AWS Lambda function means handling three things correctly: where the API key lives, how the Lambda runtime makes the outbound HTTPS request, and how you deal with Lambda's execution limits (timeout, payload size, and no persistent streaming). Get those three right and the rest is a normal Node.js or Python function.

This guide walks through a working Lambda setup end to end — packaging, secrets, a handler that calls Claude, and the deployment commands — plus the specific pitfalls that trip people up on serverless (cold starts, response size, and timeouts on long completions).

Prerequisites

Step 1: Store the API key securely

Never hardcode the key in your deployment package or set it as a plain environment variable in the console if you can avoid it. Use AWS Secrets Manager or SSM Parameter Store:

aws secretsmanager create-secret \
  --name claude/api-key \
  --secret-string '{"CLAUDE_API_KEY":"sk-ant-..."}'

Grant the Lambda execution role secretsmanager:GetSecretValue scoped to that specific secret ARN — not a wildcard.

Step 2: Write the handler

Here's a minimal Node.js handler that reads the key once per cold start and reuses it across warm invocations:

const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

let cachedKey;

async function getApiKey() {
  if (cachedKey) return cachedKey;
  const client = new SecretsManagerClient({});
  const res = await client.send(
    new GetSecretValueCommand({ SecretId: "claude/api-key" })
  );
  cachedKey = JSON.parse(res.SecretString).CLAUDE_API_KEY;
  return cachedKey;
}

exports.handler = async (event) => {
  const apiKey = await getApiKey();
  const body = JSON.parse(event.body || "{}");

  const response = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": apiKey,
      "anthropic-version": "2023-06-01",
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1024,
      messages: [{ role: "user", content: body.prompt }],
    }),
  });

  const data = await response.json();

  return {
    statusCode: 200,
    body: JSON.stringify(data),
  };
};

Caching the key at module scope avoids a Secrets Manager call on every warm invocation — this matters because Secrets Manager calls add 50-150ms of latency you don't want on every request.

Step 3: Set a realistic timeout and memory

Lambda's default timeout is 3 seconds, which is far too short for an LLM call. Set:

aws lambda update-function-configuration \
  --function-name claude-handler \
  --timeout 60 \
  --memory-size 512

For longer completions (large max_tokens, complex tool use chains), 60 seconds is a safer floor than 15–30. If you're fronting this with API Gateway, remember API Gateway REST APIs have a hard 29-second timeout regardless of your Lambda setting — use a Lambda Function URL or an ALB in front instead if you need longer.

Step 4: Deploy with SAM

A minimal template.yaml:

Resources:
  ClaudeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs18.x
      Timeout: 60
      MemorySize: 512
      Policies:
        - AWSSecretsManagerGetSecretValuePolicy:
            SecretArn: !Ref ClaudeApiKeySecret
      FunctionUrlConfig:
        AuthType: AWS_IAM

Deploy with:

sam build && sam deploy --guided

Using a Lambda Function URL instead of API Gateway avoids the 29-second cap and simplifies the setup if you don't need request transformation or custom domains out of the box.

Common pitfalls on Lambda specifically

Where a gateway simplifies this

A lot of the work above — key storage, retry logic, request logging — is boilerplate you end up rebuilding on every project. If you'd rather skip managing secrets rotation and raw x-api-key headers inside Lambda, SubToAPI gives you a generated sub_live_... key per application, so your Lambda function just calls https://api.subtoapi.app/v1/messages with a standard Authorization: Bearer header instead of handling Anthropic-specific headers and key storage yourself. It also gives you usage metadata per key, which is useful when a Lambda-based integration is shared across multiple services or teams. See the quickstart and messages endpoint docs for the exact request shape.

questions

Can I stream Claude responses from a standard Lambda function? Not with a normal synchronous invocation — the response is buffered. You need Lambda response streaming via a Function URL configured with RESPONSE_STREAM invoke mode, or run the call from a non-Lambda long-lived process. See streaming for how response streaming works at the API level.

Why does my Lambda time out even though I set a 60-second limit? If you're using API Gateway REST APIs in front of the function, that service enforces a hard 29-second cap independent of your Lambda configuration. Switch to a Lambda Function URL or an Application Load Balancer if you need longer execution windows.

Should I store the Claude API key as a Lambda environment variable or in Secrets Manager? Secrets Manager (or SSM Parameter Store) is safer — environment variables are visible to anyone with console/CLI read access to the function configuration and show up in CloudFormation templates and logs more easily than a scoped secret reference.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →