Docs / How Agents Talk to Models / One request, one response
One request, one response
POST carrying a model name and a messages array. The reply is one JSON object whose usage field is the meter: input_tokens read, output_tokens written. The model keeps no memory of the call — that one fact drives every cost in this page.The wire, in full
A language model is a stateless function: text in, text out, no memory of the last call. You talk to it over HTTP. The two dialects you'll meet are Anthropic's POST /v1/messages and OpenAI's POST /v1/chat/completions — same idea, different field names. Here is the whole thing, provider-direct, nothing clever:
# a single call, straight to the provider
curl https://api.anthropic.com/v1/messages \
-H 'content-type: application/json' \
-H 'x-api-key: $KEY' -H 'anthropic-version: 2023-06-01' \
-d '{
"model": "claude-haiku-4-5",
"max_tokens": 256,
"system": "You are a terse senior engineer. Answer in one line.",
"messages": [{"role": "user", "content": "Does POST /orders reject a request with no total field?"}]
}'That is the developer's first move on our running task — just asking, before any agent touches the code. Two fields beyond messages are worth naming now. max_tokens is the hard ceiling on the output — the model will be cut off there (more on that below). The optional system field is the system prompt: standing instructions, separate from the conversation, that ride along as input on every call. It's the first thing loaded and the last thing that changes — which (jump to caching) makes it prime cache material.
Same wire, different front doors
The curl above hits Anthropic directly, but the body — model, messages, tools — is identical wherever Claude runs. Only the front door changes:
| Access | Endpoint & auth | Where the model id goes |
|---|---|---|
| Anthropic API | api.anthropic.com · x-api-key | in the body ("model") |
| Amazon Bedrock | bedrock-runtime.<region>… · AWS SigV4 (IAM) | in the URL path; body carries anthropic_version: "bedrock-2023-05-31" |
| Google Vertex | Vertex AI endpoint · Google OAuth | in the URL path; body carries anthropic_version: "vertex-2023-10-16" |
The response is one JSON object. The part that matters for cost is usage — the model reports how many tokens it read (input) and how many it wrote (output):
{
"id": "msg_01F3k…",
"role": "assistant",
"model": "claude-haiku-4-5",
"content": [{ "type": "text", "text": "No — it reads req.body.total without a presence check." }],
"stop_reason": "end_turn",
"usage": { "input_tokens": 32, "output_tokens": 14 } // ← the meter
}Reading the response
Four parts matter. id and model echo the call. content is an array of blocks — a text block here, but it can be a tool_use block (tools), or several blocks at once. stop_reason says why it ended (below). And usage is the meter — input_tokens and output_tokens, plus cache_creation_input_tokens and cache_read_input_tokens once caching is on. If you log one field per call, log usage.
When the model stops: stop_reason
Every response says why it ended — and a truncated reply still looks like a reply, so always check it before trusting the body:
stop_reason | Means | You do |
|---|---|---|
end_turn | finished naturally | use the answer |
tool_use | wants a tool run first | run it, call again with the result |
max_tokens | hit your output ceiling — cut off mid-sentence | don't trust the body; raise max_tokens and retry |
stop_sequence | hit a stop string you set | use the answer up to it |
The max_tokens row is the quiet failure mode — you were billed for every token written before the cut, and it bites hardest with structured output, where half a JSON object doesn't parse (structured outputs).
max_tokens as “how long an answer I want” and setting it low to save money. It's a guillotine, not a hint — the model doesn't aim to finish under it, it just stops when it hits it. Set it comfortably above the longest valid answer and always check stop_reason == "max_tokens" before trusting the body.usage object is the only cost signal you get, and the provider hands it to you on every response. input_tokens is everything the model read — your system prompt, tools, the whole message history, this turn's text — and output_tokens is only what it just wrote. If you log one thing per call, log usage."orders.py" might be three tokens, a curly brace one, a run of whitespace one. Rough rule of thumb for English prose and code: ~4 characters per token, or ~0.75 tokens per word. Punctuation-dense code trends higher. You never count them by hand — usage is ground truth — but the 4-chars mental model tells you why a 300-line file lands around 3–4k input tokens.POST; the reply's usage field reports input vs output tokens, and the model forgets everything the instant it answers.