Claude API HTTP Endpoint Setup Tutorial
What "setting up the Claude API HTTP endpoint" actually means
If you're searching for this, you want to send a plain HTTP request to Claude and get a response back — no SDK, no wrapper, just an endpoint URL, some headers, and a JSON body. That's entirely possible: Claude is exposed as a standard REST-style HTTP API. You POST a JSON payload to a messages endpoint with an API key in the headers, and you get back a JSON response (or a stream of server-sent events if you ask for streaming).
This guide walks through the exact structure of that request — endpoint URL, required headers, request body shape, response format, and the common mistakes that cause 400s and 401s — so you can get a working call in minutes, whether you're hitting the endpoint directly or through a proxy like SubToAPI that turns it into a simpler key-based setup.
The anatomy of a Claude API HTTP request
Every request to a Claude-style messages endpoint has four parts:
- Method:
POST - URL: the base API URL plus a versioned path, e.g.
/v1/messages - Headers: authentication, content type, and (depending on provider) a version header
- Body: a JSON object with the model name, a
messagesarray, and amax_tokensvalue
A minimal header set looks like this:
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
And a minimal body:
{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Explain HTTP status codes in two sentences." }
]
}
That's the whole shape. Everything else — system prompts, tool definitions, streaming flags, temperature — is additive on top of this skeleton.
Step 1: Get an API key and confirm your base URL
You need a key before anything else. If you're setting this up through SubToAPI, you generate a sub_live_... key from the dashboard after signing up at /signup — no separate billing account or model access request needed, since it runs on top of your existing Claude access. The base URL in that case is https://api.subtoapi.app/v1/.
If you're hitting a provider endpoint directly, your base URL and key come from that provider's console instead. Either way, the request shape below is the same.
Step 2: Make your first raw HTTP call
Here's a full curl example against SubToAPI's endpoint:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 512,
"messages": [
{ "role": "user", "content": "Write a one-line Docker healthcheck command." }
]
}'
A successful response is a JSON object with the generated text, a stop_reason, and token usage:
{
"id": "msg_01...",
"role": "assistant",
"content": [
{ "type": "text", "text": "HEALTHCHECK CMD curl -f http://localhost/ || exit 1" }
],
"stop_reason": "end_turn",
"usage": { "input_tokens": 18, "output_tokens": 14 }
}
If you're setting this up for the first time, keep the response printed to your terminal until you understand each field — stop_reason tells you why generation ended, and usage is what you'll later use for cost tracking.
Step 3: Wire it into JavaScript
Most endpoint setup problems aren't the API itself — they're environment variables, missing headers, or malformed JSON bodies in application code. Here's a clean fetch-based setup:
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 512,
messages: [
{ role: "user", content: "Summarize this ticket in one sentence." }
]
})
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
const data = await response.json();
console.log(data.content[0].text);
Full reference for request and response fields lives at /docs/messages. If you're doing this setup for the first time, /docs/quickstart covers key generation and your first call end to end.
Step 4: Add streaming if you need it
For chat UIs or anything latency-sensitive, you don't want to wait for the full response before rendering anything. Set "stream": true in the body and read the response as server-sent events instead of plain JSON:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 512,
"stream": true,
"messages": [{ "role": "user", "content": "Count to five." }]
}'
Each event is a chunk prefixed with event: and data: lines. Parsing them correctly — especially handling partial tool-use blocks — is the part most people get wrong on a first setup. The full event sequence and a parsing example are documented at /docs/streaming.
Step 5: Handle errors deliberately
Don't let your endpoint setup silently swallow errors. The common ones:
- 401 — bad or missing
Authorizationheader. Check the key is actually being read from the environment, not a stale shell variable. - 400 — malformed JSON body, usually a missing
max_tokensor wrongrolevalue. - 429 — rate limited. Back off and retry with jitter rather than hammering the endpoint in a loop.
- 529/503 — upstream overload. Treat these as retryable.
A production-ready setup wraps the fetch/curl call in retry logic for 429/5xx and fails fast on 400/401, since those indicate a setup bug rather than a transient issue.
Step 6: Move beyond a single hardcoded key
Once the basic endpoint call works, the next problems are usually: separate keys per environment, per-team usage visibility, and not wanting to manage billing and rate limits yourself. That's the layer SubToAPI adds on top of the raw HTTP setup — scoped sub_live_... keys per app or environment, usage metadata on every response, and team seats, all through the same /v1/messages shape you just tested. Plans start at €9/month on Solo, with Team and Scale tiers for multi-seat setups — see /pricing for details, or start a free trial at /signup.
Questions
Do I need an SDK to call the Claude API endpoint? No. The endpoint is plain HTTPS with a JSON body, so curl, fetch, or any HTTP client in any language works. SDKs just wrap this same request.
Why am I getting a 401 after setting my API key correctly? Usually the key isn't actually reaching the request — check for trailing whitespace, a stale environment variable, or a missing Bearer prefix in the Authorization header.
Can I test the endpoint without writing code? Yes — curl from a terminal is the fastest way to confirm your key, headers, and body are correct before wiring anything into an application.