Claude API on Vercel Edge Functions: Setup Guide
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:
- Low latency to the request origin — the function runs in a region close to the user, not just close to your database.
- Streaming is native — the Edge runtime is built around the Web
ReadableStream/Responsestreaming model, which matches how Claude responses work. - No server to manage — you get auto-scaling without configuring instances.
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:
process.env.ANTHROPIC_API_KEYmust be set in the Vercel dashboard and marked available for the environment you're deploying (Production/Preview/Development).- Passing
upstream.bodystraight through as theResponsebody works because both sides speak the Web Streams API — no manual chunk parsing needed for a simple pass-through proxy. stream: trueon the request body is what makes Claude emit server-sent events instead of a single JSON blob.
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
- Set
ANTHROPIC_API_KEY(orSUBTOAPI_KEY) in Vercel's environment variables for every environment you deploy to. - Add
export const runtime = 'edge';to every route file that calls the API — it's not inherited across files. - Test streaming locally with
vercel devbefore deploying, since local Node dev servers sometimes mask Edge-specific errors. - If you need tool use or multi-turn context, check the tools docs or messages docs for the exact payload shape before wiring it into your Edge route.
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.