← Blog

Claude API on AWS Lambda: A Serverless Setup Guide

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

If you're building a serverless app and want to call the Claude API from AWS Lambda, the short answer is: it works well for request/response workloads, but you need to plan around three things — execution timeouts, streaming limitations, and secret management. This article walks through a working setup and the gotchas that trip people up when they move Claude calls into Lambda.

Serverless is a natural fit for Claude-powered endpoints: you don't need a long-running server just to proxy chat completions, and you only pay for compute when a request comes in. But Lambda's execution model (short-lived, stateless, timeout-bound) doesn't map perfectly onto LLM calls that can take 10–60+ seconds, especially with longer outputs or tool use. The rest of this guide covers how to make that work in practice.

Basic Lambda Handler for the Claude API

A minimal Node.js Lambda function that calls the Claude API directly looks like this:

export const handler = async (event) => {
  const body = JSON.parse(event.body);

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

  const data = await response.json();

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

This works fine for short, non-streaming requests. The problems start when you need longer responses, streaming, or when you want multiple apps/teams to share one Claude account without juggling raw API keys inside every function.

Timeout and Concurrency Limits to Plan For

The practical takeaway: use Lambda Function URLs instead of API Gateway when you need generations longer than 29 seconds, or offload long jobs to an async pattern (SQS + Lambda + a webhook/polling endpoint) so nothing is blocked on a synchronous HTTP response.

Streaming From Lambda

Claude's streaming API (stream: true) sends Server-Sent Events as tokens are generated. This is great for perceived latency, but Lambda's standard invocation model buffers the entire response before returning it — which defeats the purpose of streaming.

AWS added Lambda response streaming (via Function URLs with InvokeMode: RESPONSE_STREAM) specifically for this use case. A streaming-capable handler looks roughly like:

export const handler = awslambda.streamifyResponse(
  async (event, responseStream, context) => {
    const upstream = await fetch("https://api.anthropic.com/v1/messages", {
      method: "POST",
      headers: {
        "x-api-key": process.env.ANTHROPIC_API_KEY,
        "anthropic-version": "2023-06-01",
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model: "claude-sonnet-4-5",
        max_tokens: 1024,
        stream: true,
        messages: JSON.parse(event.body).messages,
      }),
    });

    for await (const chunk of upstream.body) {
      responseStream.write(chunk);
    }
    responseStream.end();
  }
);

This requires a Function URL, not API Gateway — API Gateway doesn't support streaming responses the same way. It also means you lose some of API Gateway's built-in auth and throttling features, so you have to handle those yourself in the function.

Where SubToAPI Fits In

A lot of the complexity above — key management, streaming, usage visibility across functions — is exactly what SubToAPI is built to remove. Instead of putting your raw Anthropic API key in every Lambda's environment variables (and rotating it everywhere when it leaks or changes), you generate a scoped sub_live_... key per function or per team through the SubToAPI dashboard, and call one stable endpoint:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Summarize this invoice."}]
  }'

Because the API shape matches what you'd expect from a Claude-compatible endpoint, swapping it into the Lambda handler above is a one-line change. You get streaming support (see /docs/streaming), tool use (see /docs/tools), and per-key usage metadata — useful when you have multiple Lambda functions across teams or environments all calling Claude and you want to know which one is driving cost. Pricing starts at Solo for €9/month, with Team and Scale tiers for per-seat access; see /pricing for details, or start with a free trial at /signup.

A Practical Architecture Pattern

For most serverless Claude integrations, this pattern holds up well:

  1. Short, synchronous requests (classification, short Q&A, structured extraction) → API Gateway + standard Lambda, 10–15 second budget.
  2. Long or streaming responses → Lambda Function URL with response streaming enabled, no API Gateway in front.
  3. Batch or background jobs (document processing, bulk generation) → SQS trigger + Lambda, write results to S3/DynamoDB, notify via webhook or polling.
  4. Secrets → store the API key (Anthropic's or a SubToAPI application key) in AWS Secrets Manager or Lambda environment variables, never hardcoded, and rotate per environment (dev/staging/prod).

Start with pattern 1. Only move to streaming or async patterns once you've confirmed your actual response times and payload sizes require it — premature complexity in a serverless Claude setup is a common source of debugging pain later.

Questions

Can I stream Claude API responses through API Gateway on Lambda? Not reliably. API Gateway buffers responses, so true token-by-token streaming requires a Lambda Function URL with RESPONSE_STREAM invoke mode instead of API Gateway.

What's the main timeout risk when running Claude on Lambda? API Gateway's 29-second integration timeout is usually the first limit you hit, not Lambda's own 15-minute cap. Long generations or tool-use loops can exceed it.

Is it safe to store the Claude API key as a Lambda environment variable? It works, but it's better practice to use AWS Secrets Manager or a scoped key (like a SubToAPI application key) so you can rotate or revoke access per function without redeploying every Lambda that uses it.

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 →