How to Build a Multi-Agent System with Claude API
Building a multi-agent system with Claude API means splitting a complex task into smaller jobs, assigning each job to a specialized "agent" (a distinct prompt, role, and toolset), and coordinating how they pass information to each other. There's no special "multi-agent" endpoint in the Claude API — it's an architecture pattern you build on top of regular Messages API calls, where each agent is really just a Claude request with its own system prompt, context, and sometimes its own tools.
This matters because a single monolithic prompt tends to break down once a task requires multiple distinct skills — research, writing, code review, data extraction — especially when each skill benefits from a different system prompt, temperature, or tool set. Multi-agent design fixes this by giving each responsibility its own isolated context and letting an orchestrator route work between agents. Below is a practical walkthrough of how to structure this, from the simplest orchestrator pattern to tool-using agents that hand off work to each other.
Core Patterns for Multi-Agent Systems
There are three patterns that cover almost every real-world multi-agent setup:
- Orchestrator-worker: one "manager" agent decomposes a task and dispatches subtasks to specialized worker agents, then merges results.
- Pipeline: agents run in a fixed sequence, each one consuming the previous agent's output (e.g., researcher → writer → editor).
- Debate/critique: two agents (or an agent and a critic) iterate on the same artifact until a quality bar is met.
All three are implemented the same way at the API level: you make separate Claude API calls with different system prompts, and your application code manages state and routing between them. The "agent" abstraction lives entirely in your orchestration code, not in the API itself.
Step 1: Define Each Agent's Role and System Prompt
Each agent should have a narrow, well-defined responsibility. Vague roles cause agents to overlap and contradict each other.
const agents = {
researcher: {
system: "You are a research agent. Given a topic, return 5 factual bullet points with sources. Do not write prose.",
},
writer: {
system: "You are a writing agent. Turn research bullet points into a 300-word article section. Do not invent facts not in the input.",
},
editor: {
system: "You are an editing agent. Review the draft for clarity and factual consistency with the research. Return a corrected version.",
},
};
Keep each system prompt short and task-specific. This is more important in multi-agent setups than in single-agent chatbots, because ambiguity compounds across hops.
Step 2: Build the Orchestrator
The orchestrator is just application code that calls Claude multiple times, passing the output of one agent as input to the next.
async function callAgent(agent, userInput) {
const res = 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-5",
system: agent.system,
max_tokens: 1024,
messages: [{ role: "user", content: userInput }],
}),
});
const data = await res.json();
return data.content[0].text;
}
async function runPipeline(topic) {
const research = await callAgent(agents.researcher, topic);
const draft = await callAgent(agents.writer, research);
const final = await callAgent(agents.editor, draft);
return final;
}
This is a pipeline pattern. For an orchestrator-worker pattern, replace the fixed sequence with a manager agent that first decides which workers to call and in what order, then invoke those workers dynamically based on its output.
Step 3: Give Agents Tools Where Needed
Some agents need to act in the world — query a database, call an external API, search the web — rather than just transform text. This is where Claude's tool use comes in: define a tool schema for that agent, let Claude decide when to call it, execute the tool in your code, and feed the result back.
const tools = [
{
name: "search_docs",
description: "Search internal documentation for a query",
input_schema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
},
];
Only give tools to agents that actually need them. A writer agent that rewrites text doesn't need database access, and giving it unnecessary tools increases the chance of unpredictable tool calls. See /docs/tools for schema details and request format.
Step 4: Manage State Between Agents
As agents multiply, so does the context you need to pass between them. Don't dump the entire conversation history into every agent call — pass only what that specific agent needs. This keeps token usage predictable and avoids one agent getting confused by irrelevant context from another agent's job. A simple shared object works well:
let state = { topic, research: null, draft: null, final: null };
state.research = await callAgent(agents.researcher, state.topic);
state.draft = await callAgent(agents.writer, state.research);
state.final = await callAgent(agents.editor, state.draft);
Step 5: Handle Failures Per Agent
In a multi-step system, a single malformed response can cascade through the whole pipeline. Validate each agent's output before passing it forward — check for expected structure, minimum length, or required fields — and retry that specific agent call if validation fails, rather than restarting the entire pipeline.
Running This in Production
Once you move past a prototype, you'll want centralized visibility into every agent call: which model was used, how many tokens each step consumed, and which agent failed. Running each agent through SubToAPI gives you one dashboard for all of this — usage metadata per request, streaming support for long agent outputs, and application API keys (sub_live_...) so you can isolate billing and rate limits per agent or per team. If you're coordinating several agents that each make frequent calls, that per-key visibility makes debugging a stuck pipeline much faster than digging through raw logs. Check /docs/quickstart to get an API key running, and /docs/messages for the full request format used in the examples above.
Frequently Asked Questions
Do I need a special framework to build a multi-agent system with Claude API? No. Frameworks like LangGraph or CrewAI can help manage orchestration logic, but a multi-agent system is fundamentally just multiple Claude API calls coordinated by your own code — you can start with plain JavaScript or Python functions.
How many agents should a multi-agent system have? Start with as few as possible — usually 2 to 4. Each additional agent adds latency and failure points. Only split a task into a new agent when a single prompt genuinely can't handle both responsibilities well.
Can agents call each other directly instead of going through an orchestrator? Technically yes, but it makes debugging and cost tracking much harder. A central orchestrator that manages routing, state, and error handling is easier to maintain and lets you log every step in one place.