Mastering "Production-Like" Features With the Claude API
The Claude API has not only text generation but also practical features such as streaming responses, Tool Use (function calling), structured JSON output, Prompt Caching, and the Batch API. As of 2025, rather than turning a prompt tried in a chat UI into an API as-is, an implementation that designs response format, speed, cost, and reproducibility is important.
In this article, with Python-centered code examples, we organize "how to integrate it so it is actually easy to use." Since fine differences in the SDK are updated, always read the official documentation together when adopting.
SSE Streaming: First, Create an Experience That "Doesn't Make Users Wait"
For longish answers and summary generation, displaying progressively with Server-Sent Events (SSE) gives better UX than waiting for the full text to complete. It is especially effective for chat, review support, and minutes generation.
The idea of a basic implementation
- Receive the Claude API stream on the backend
- Relay to the frontend as-is, or format and send
- Handle not just fragment text but also completion events and error events
from anthropic import Anthropic
client = Anthropic(api_key="YOUR_API_KEY")
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1200,
temperature=0.2,
messages=[
{"role": "user", "content": "Tell me 3 key points for implementing SSE streaming"}
]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final_message = stream.get_final_message()
The point at implementation time is not to store fragments as-is. Since intermediate output mixes in restatements and unfinished sentences, it is safer to use the final confirmed text for DB storage and audit logs.
Cases streaming suits and doesn't suit
| Case | Suitability | Reason |
|---|---|---|
| Chat UI | Suits | Perceived speed improves greatly |
| Long-text summarization | Suits | Users can start reading partway |
| JSON structured output | Slight caution | Intermediate fragments tend to be incomplete JSON |
| Batch aggregation | Doesn't suit | The merit of progressive display is thin |
Especially for processing that uses structured output strictly, the practical separation is: streaming display is UI-only, and business processing is done after the final response is confirmed.
Tool Use: "Don't Leave External Processing Too Much to the Model"
Tool Use is a mechanism where Claude "chooses the needed function and builds the arguments." Common uses are internal search, inventory lookup, weather retrieval, RAG search, DB queries. However, what matters is not letting the AI execute freely but controlling on the app side which tool, under what conditions, and how far it is allowed.
Example of a tool definition
tools = [
{
"name": "get_weather",
"description": "Get the current weather of a specified city",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name. e.g., Tokyo"}
},
"required": ["city"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=800,
tools=tools,
messages=[
{"role": "user", "content": "Check the weather in Tokyo and also tell me a clothing guideline"}
]
)
If a tool use request is included in the response, the app side executes the actual function and returns its result to Claude again.
def get_weather(city: str):
return {"city": city, "weather": "sunny", "temp_c": 27}
# Pseudo-code: extract tool_use from response
tool_result = get_weather("Tokyo")
followup = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=800,
messages=[
{"role": "user", "content": "Check the weather in Tokyo and also tell me a clothing guideline"},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": str(tool_result)
}]
}
]
)
Practical points of Tool Use design
- Insert a confirmation step for processing with side effects (send/update/delete)
- Strictly define tool arguments with JSON Schema
- Don't pass search results as-is; format to the necessary minimum
- On failure, "returning the reason for failure" is more stable than "retrying"
What beginners especially tend to do is cram everything into one all-purpose tool. In reality, separating a search tool, a reference tool, and an update tool reduces misinvocation and permission accidents.
JSON Structured Output: Receive It in a Form Easy to Post-Process
For business-system integration, stable JSON is more important than natural prose. For example, inquiry classification, FAQ extraction, and ticket priority judgment make downstream work easier if you fix the output schema from the start.
Prompt example
You are an inquiry classification API.
You must return only JSON.
Schema:
{
"category": "billing | technical | sales | other",
"priority": 1,
"summary": "string"
}
Inquiry body:
"The invoice amount differs from last month. Please check."
In practice, state "do not return anything other than JSON" and specify enumerated values, numeric ranges, and required fields. Further, also perform validation on the app side.
import json
from pydantic import BaseModel, ValidationError
class TicketResult(BaseModel):
category: str
priority: int
summary: str
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=300,
temperature=0,
messages=[{"role": "user", "content": prompt}]
)
text = "".join(
block.text for block in resp.content if getattr(block, "type", "") == "text"
)
data = json.loads(text)
result = TicketResult(**data)
print(result)
Keep temperature low, ideally 0–0.2, to suppress format drift. Note that returning UI-facing explanatory text and JSON at the same time tends to break, so separating the explanation API and the structured API is the standard approach.
Prompt Caching: Lower the Cost of Long Common Prompts
Sending a system prompt, long business rules, or a product spec every time increases latency and cost. That is where Prompt Caching is effective. Making the long common premise text a cache target and structuring it to be called repeatedly tends to produce effects for high-frequency APIs.
- Suited to: long guidelines, common knowledge, fixed templates
- Doesn't suit: prompts whose content changes almost entirely every time
In implementation, "how much to make the fixed part" matters. For example, fixing internal rules, output rules, classification criteria and making only user input variable raises the cache reuse rate.
Batch API: Think of Large-Volume Processing Separately From the Synchronous API
For asynchronous processing of hundreds to tens of thousands of items—summarizing review comments, auto-classifying inquiries, generating product descriptions—the Batch API is suited. Real-time responsiveness drops, but it is easy to operate and rate control is easier, which are advantages.
Rough guide for dividing use
| Method | Suited use | Characteristics |
|---|---|---|
| Normal API | Chat, instant response | For real-time |
| Streaming | Long-text generation UI | High perceived speed |
| Batch API | Nightly processing, bulk classification | Suited to large-scale processing |
In the field, dividing as Streaming for processing where the user waits and Batch for internal back-office processing makes design easier.
A Recommended Configuration at Implementation Time
- UI layer: streaming display, cancel operation
- API layer: Claude calls, retry, rate control
- Tool layer: separate search/reference/update
- Validation layer: JSON validation, audit logs, permission checks
With this separation, even if you later change the model or improve prompts, you can keep the impact on the whole app small. The Claude API has many advanced features, but you don't need to put them all in from the start. The recommendation is phased adoption in the order 1) JSON structured output then 2) Streaming then 3) Tool Use then 4) Caching/Batch. This is the least failure-prone and most visibly effective way to proceed.



