How to Deploy Claude API on AWS Lambda
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:
- Event-driven AI: run Claude in response to S3 uploads, SQS messages, cron jobs, or webhooks.
- No idle cost: you only pay for invocation time, which matters for low-traffic or bursty workloads.
- Easy integration with existing AWS infra: API Gateway, Step Functions, DynamoDB, etc.
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
- An AWS account with permissions to create Lambda functions, IAM roles, and (optionally) API Gateway.
- Node.js 18+ or Python 3.11+ installed locally.
- An Anthropic API key, or a SubToAPI key if you'd rather call Claude through a standard HTTPS endpoint with built-in usage metadata. Either works for the code below — just swap the base URL and key.
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
- Cold starts for Node 18 functions are typically 100–400ms — negligible next to a multi-second Claude response.
- Set reserved concurrency if you need to cap how many simultaneous Claude calls your function makes, which also protects you from runaway cost during traffic spikes.
- If you're calling Claude from many Lambda invocations concurrently, make sure your API key's rate limits can handle the burst, or queue requests through SQS with a Lambda consumer to smooth traffic.
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.