Claude API Local Development Proxy Setup Guide
Why you need a proxy for local Claude API development
If you're building a frontend that talks to Claude, you can't call Anthropic's API directly from the browser — there's no CORS support for client-side requests, and even if there were, exposing your API key in client-side JavaScript is a security problem. A local development proxy sits between your frontend (or mobile app, or test scripts) and the Claude API, forwarding requests while keeping your key server-side.
This guide covers the practical setup: a minimal Node.js proxy you can run on localhost, how to handle streaming and tool calls through it, and when it makes more sense to skip the DIY proxy entirely and use a hosted API layer instead.
What a local Claude API proxy actually does
A proxy for local development typically needs to:
- Accept requests from your app running on
localhost:3000(or wherever) - Attach the real API key server-side so it never touches the browser
- Forward the request to Anthropic's endpoint
- Stream the response back without buffering it (critical for chat UIs)
- Optionally log request/response payloads so you can debug prompts without opening a network inspector tab 50 times
It's not complicated, but it's easy to get the streaming and headers wrong, which is where most setup issues come from.
Minimal proxy with Node and Express
Here's a bare-bones proxy you can run locally in five minutes:
import express from "express";
import cors from "cors";
const app = express();
app.use(cors({ origin: "http://localhost:3000" }));
app.use(express.json());
app.post("/proxy/messages", async (req, res) => {
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(req.body),
});
// Pass streaming responses through untouched
res.status(upstream.status);
upstream.body.pipe(res);
});
app.listen(8787, () => console.log("Proxy running on :8787"));
Point your frontend at http://localhost:8787/proxy/messages instead of the real Anthropic endpoint. The key stays in your .env file and never ships to the browser.
Handling streaming correctly
The most common mistake is buffering the response before sending it. If you call .json() on the upstream response or wait for it to fully resolve before writing to res, you lose streaming entirely and your UI will feel frozen until the whole completion arrives. Piping the body stream directly (as above) preserves server-sent events so your frontend can render tokens as they arrive.
Passing through tool use and multi-turn context
If your app uses tool calling, the proxy doesn't need to understand the tool schema at all — it just forwards whatever JSON body your app sends. The complexity lives in your application code, not the proxy. Keep the proxy dumb: validate the request shape minimally, forward it, stream the response back.
Adding request logging for debugging
During local development it's useful to log exactly what you sent and what came back, especially when iterating on prompts:
app.post("/proxy/messages", async (req, res) => {
console.log("REQUEST:", JSON.stringify(req.body, null, 2));
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(req.body),
});
res.status(upstream.status);
upstream.body.pipe(res);
});
For streaming responses, logging the full body requires tee-ing the stream, which adds complexity. A simpler approach for local debugging is to log only non-streaming test requests, and inspect streaming output visually in your UI.
When a hosted proxy makes more sense
A local proxy solves the "don't leak the key to the browser" problem, but it doesn't solve a few things that come up as soon as more than one person is involved:
- Multiple developers sharing one Anthropic key with no way to see who used what
- No usage breakdown per environment (dev, staging, a teammate's laptop)
- Rebuilding the same CORS/streaming/logging plumbing on every new project
This is the gap SubToAPI fills. Instead of proxying straight to Anthropic with a shared raw key, you generate scoped sub_live_... keys per developer or per environment through the dashboard, and point your local proxy — or your app directly — at https://api.subtoapi.app/v1/messages. You get the same streaming and tool-use behavior, plus usage metadata per key, without maintaining your own forwarding server:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello from local dev"}]
}'
This is handy if you're prototyping locally but want production-ready auth, streaming, and per-developer keys without writing proxy code at all. Check the quickstart for setup, or the messages, streaming, and tools docs for request shapes identical to what you'd send Anthropic directly. Plans start at €9/month for solo use, with team seats for shared development environments.
Environment-based switching
Whichever approach you take, keep the proxy URL in an environment variable so you can swap between local, staging, and production without code changes:
const API_BASE = process.env.CLAUDE_API_BASE || "http://localhost:8787/proxy";
This also makes it trivial to point the same codebase at a self-hosted proxy during early development and at SubToAPI once you need multi-developer key management — just change CLAUDE_API_BASE.
questions
Do I need a proxy if I'm only calling the Claude API from a backend? No. If your API key stays in server-side code and never reaches the browser, you can call Anthropic's endpoint directly. Proxies matter when a frontend, mobile app, or browser extension needs to make the request.
Will a local proxy break streaming responses? Only if it buffers the response body. Pipe the upstream stream directly to the client response instead of waiting for it to resolve, and SSE streaming works the same as calling the API directly.
Can I use the same proxy setup for local development and production? Technically yes, but it's better to separate them. Use a local proxy or scoped keys for development, and a dedicated, monitored endpoint — whether self-hosted or via SubToAPI — for production traffic so you get real usage visibility.