← Blog

How to Deploy Claude API on AWS Lambda

2026-10-29 · 5 min read · SubToAPI Team

Deploying Claude API on AWS Lambda means wrapping your Anthropic API calls in a serverless function so you can trigger Claude from API Gateway, EventBridge, S3 events, or any other AWS trigger without running a server. The core pattern is simple: a Lambda handler receives a request, calls the Claude Messages API with your API key, and returns the response — but there are a handful of Lambda-specific gotchas (timeouts, cold starts, streaming limitations, secrets management) that trip people up.

This guide walks through a working deployment end to end: project setup, the handler code, packaging, deployment via the AWS CLI or SAM, and the operational details that matter once you're running this in production.

Why deploy Claude on Lambda

Lambda is a good fit for Claude integrations when you want:

It's a poor fit if you need long-running streaming responses to a browser (Lambda has execution and payload limits that fight against SSE streaming) or very high, steady traffic where a persistent container is cheaper and simpler to scale.

Prerequisites

Step 1: Project structure

claude-lambda/
├── index.js
├── package.json
└── template.yaml

Install the only dependency you need — a fetch-capable runtime is built into Node 18+, so no SDK is required unless you want one.

Step 2: Write the handler

// index.js
export const handler = async (event) => {
  const body = JSON.parse(event.body || "{}");
  const userMessage = body.message || "Say hello.";

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

  if (!response.ok) {
    const errText = await response.text();
    return { statusCode: response.status, body: errText };
  }

  const data = await response.json();
  return {
    statusCode: 200,
    headers: { "content-type": "application/json" },
    body: JSON.stringify(data),
  };
};

This is a plain, non-streaming call. Keep max_tokens conservative for your first deploy — large completions can push you close to Lambda's execution timeout.

Step 3: Secrets, not environment variables in plaintext

Don't hardcode your API key in template.yaml or commit it to the repo. Store it in AWS Secrets Manager or SSM Parameter Store, then read it at cold start:

import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";

let cachedKey;

async function getApiKey() {
  if (cachedKey) return cachedKey;
  const client = new SSMClient({});
  const result = await client.send(
    new GetParameterCommand({ Name: "/claude/api-key", WithDecryption: true })
  );
  cachedKey = result.Parameter.Value;
  return cachedKey;
}

Caching the key outside the handler body means it's fetched once per warm container, not on every invocation.

Step 4: Package and deploy with SAM

A minimal template.yaml:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
  ClaudeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs18.x
      Timeout: 30
      MemorySize: 512
      Policies:
        - SSMParameterReadPolicy:
            ParameterName: claude/api-key
      Events:
        Api:
          Type: Api
          Properties:
            Path: /claude
            Method: post

Deploy with:

sam build
sam deploy --guided

SAM provisions the Lambda function, IAM role, and an API Gateway endpoint in one pass. If you'd rather not use SAM, aws lambda create-function with a zipped deployment package works identically — SAM just saves you from writing the IAM policy and API Gateway config by hand.

Step 5: Set the timeout and memory correctly

Claude API calls for longer completions can take 10–20+ seconds. The Lambda default timeout is 3 seconds — set it explicitly to at least 30 seconds, and higher if you expect large max_tokens values. Memory also affects CPU allocation; 512 MB is a reasonable starting point for a function that's mostly waiting on a network call rather than doing compute.

The streaming problem

Claude's API supports server-sent event streaming for token-by-token output, but standard Lambda invocations buffer the entire response before returning it — you lose the "typing" effect. Lambda response streaming (available via Function URLs with RESPONSE_STREAM invoke mode) can forward SSE chunks, but it adds real complexity: you need a Function URL instead of API Gateway REST, and client-side handling has to match.

If your product needs streaming to the browser and you don't want to build and maintain that plumbing, a managed layer like SubToAPI handles streaming over a standard HTTPS endpoint without you having to wire up Lambda response streaming yourself — see the streaming docs for the request format. For many Lambda-based products, it's simpler to call a service like this from your function and let it handle the streaming semantics, while your Lambda just does orchestration.

Cold starts and concurrency

Monitoring and cost control

Log token usage from each response (usage.input_tokens, usage.output_tokens) to CloudWatch so you can track spend per invocation. If you're running this across a team, managing individual Anthropic keys and reconciling usage per developer or per feature becomes tedious — a dashboard that gives per-key usage and team seats, like SubToAPI's Solo and Team plans, removes that bookkeeping without changing how your Lambda code calls the API. Getting started takes the same shape as the direct integration above — see the quickstart for the exact request format and the messages reference for parameters.

questions

Can Lambda handle Claude's streaming responses? Only with Function URLs in RESPONSE_STREAM invoke mode, which requires bypassing standard API Gateway REST integrations. For most teams, buffering the full response in a standard Lambda invocation is simpler unless real-time token streaming to the browser is a hard requirement.

What timeout should I set for a Claude Lambda function? At least 30 seconds for typical completions; increase it further if you're requesting large max_tokens values or using extended thinking, since response time scales with output length.

Do I need the Anthropic SDK, or can I use plain fetch? Plain fetch (built into Node 18+ runtimes) is enough for a single Messages API call and keeps your deployment package small. The SDK is only worth adding if you need retries, typed responses, or SDK-level streaming helpers.

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 →