← Blog

Claude API on Vercel Edge Functions: Setup Guide

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

Setting up the Claude API inside a Vercel Edge Function means writing a fetch-based handler that runs on export const runtime = 'edge', since the Edge runtime doesn't support Node.js APIs like http, net, or the Anthropic SDK's default transport in some versions. The short version: use plain fetch, stream the response with ReadableStream, and keep your API key in an environment variable scoped to the Edge runtime.

This matters because Edge Functions run on V8 isolates close to your users, not in a Node.js container. They have fast cold starts and lower latency, which is exactly what you want for a chat UI calling an LLM, but they also have a stricter execution environment. Below is a working setup, the gotchas that trip people up, and how to simplify it with a single HTTPS endpoint if you'd rather skip API key rotation and provider-specific SDK quirks.

Why Edge Functions for Claude API calls

Edge Functions are a good fit for LLM proxying because:

The tradeoff is the Edge runtime has a limited standard library. No fs, no raw TCP sockets, and a smaller max execution time depending on your Vercel plan. For a chat completion call that's rarely an issue, but it's worth knowing before you build something that assumes full Node.js.

Basic setup

Create an API route under app/api/chat/route.ts (App Router) and declare the Edge runtime explicitly:

export const runtime = 'edge';

export async function POST(req) {
  const { messages } = await req.json();

  const upstream = 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,
      stream: true,
    }),
  });

  return new Response(upstream.body, {
    headers: { 'content-type': 'text/event-stream' },
  });
}

Key points:

Handling streaming on the client

On the frontend, read the stream with a TextDecoder and parse SSE lines as they arrive:

const res = await fetch('/api/chat', {
  method: 'POST',
  body: JSON.stringify({ messages }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let text = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  text += decoder.decode(value);
  // parse `data: {...}` lines and update UI incrementally
}

This is the same pattern you'd use for any SSE-based API, Claude or otherwise — the Edge runtime doesn't change the client-side parsing logic.

Common pitfalls

Using the Anthropic Node SDK directly in an Edge Function. Older SDK versions assume a Node.js HTTP client under the hood and will throw at build or runtime inside the Edge sandbox. If you need the SDK's typed helpers, confirm it explicitly supports the Edge runtime, or stick to raw fetch calls like the example above.

Forgetting anthropic-version. Omitting this header returns a 400. It's easy to miss when you're copying a snippet that only shows the x-api-key header.

Execution time limits. Edge Functions on Vercel have a maximum duration depending on your plan. Long non-streamed generations with a high max_tokens can exceed it. Streaming avoids this because the function returns as soon as the first bytes arrive — you're not waiting for the full response to buffer before responding.

CORS and multiple origins. If your Edge Function is called from a different domain (e.g., a separate marketing site or mobile webview), you need to set Access-Control-Allow-Origin headers manually — Edge Functions don't add them by default.

A simpler alternative: one API key, no header juggling

If you're maintaining this proxy mainly to hide your Anthropic key from the client and manage usage across a team, you can skip the raw-key setup entirely. SubToAPI turns your existing Claude access into a standard HTTPS API with an sub_live_... key, so your Edge Function just forwards to one endpoint instead of managing provider headers and version strings:

export const runtime = 'edge';

export async function POST(req) {
  const { messages } = await req.json();

  const upstream = await fetch('https://api.subtoapi.app/v1/messages', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
    },
    body: JSON.stringify({
      model: 'claude-3-5-sonnet-latest',
      max_tokens: 1024,
      stream: true,
      messages,
    }),
  });

  return new Response(upstream.body, {
    headers: { 'content-type': 'text/event-stream' },
  });
}

This keeps the same streaming pattern but gives you per-key usage metadata and team seats if more than one developer is building against the same Claude access — useful if you're running this Edge Function across staging and production with different keys per environment. See the streaming docs and quickstart for the full request/response shape, or sign up to get a key.

Deployment checklist

Questions

Can I use the official Anthropic SDK in a Vercel Edge Function? Only if the SDK version explicitly supports the Edge runtime's fetch-based transport. If unsure, use raw fetch calls — they always work and avoid bundling issues.

Why does my Claude API call work locally but fail when deployed to Edge? Usually a missing environment variable for the Production/Preview environment, or Node-only APIs (like fs or SDK internals) that aren't available in the Edge sandbox.

Does streaming avoid Vercel's Edge Function duration limits? Yes, largely. Since the response starts streaming as soon as the first tokens arrive, you're not blocking on the full generation, which keeps you well under typical duration caps even for long responses.

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 →