stop_reason tells you whether an answer is complete, and usage is how you track spend.
Besides content, the response tells you why Claude stopped and how much it used.
stop_reason is a short string:
end_turn: Claude finished naturally. This is the normal case.max_tokens: Claude ran into yourmax_tokensceiling, so the answer is cut off.stop_sequence: Claude produced one of the custom strings you passed instop_sequences.tool_use: Claude wants your code to run a tool (covered later).refusal: Claude declined for safety reasons.
Always check it. Treating a max_tokens answer as complete is a classic bug: half a JSON object, or a summary that stops mid-list. Either raise max_tokens or ask for a shorter answer.
usage counts tokens. input_tokens is what you sent and output_tokens is what Claude wrote. You pay per token, with output usually priced higher than input, so these two numbers are the basis of every cost estimate. Later lessons add more fields here, such as tokens read from the prompt cache.
The response also has an id (like msg_...) and the model that answered. When something goes wrong and you contact support, the request id in message._request_id is what they will ask for, so log it alongside errors.
Example
import anthropic
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-opus-5-5",
max_tokens=30, # deliberately small
messages=[{"role": "user", "content": "Explain photosynthesis in detail."}],
)
print(msg.stop_reason) # max_tokens -> answer was cut off
print(msg.usage.input_tokens, msg.usage.output_tokens)
print(msg._request_id) # req_... for support tickets
if msg.stop_reason == "max_tokens":
print("Truncated: raise max_tokens or ask for less.")Remember
- Check stop_reason;
max_tokensmeans the answer is incomplete - usage.input_tokens and usage.output_tokens drive cost
- Log
_request_idfor debugging
Watch out for
- Parsing a truncated reply as if it were complete.