Claude API System Prompt Best Practices
A good system prompt is the difference between a Claude integration that behaves predictably and one that drifts, over-explains, or ignores your formatting rules halfway through a conversation. The system prompt is where you set persistent instructions that apply to every turn — role, tone, constraints, output format — separate from the actual user messages.
This guide covers the concrete practices that make system prompts reliable in production: how to structure them, what to include and exclude, how they interact with tool use and long context, and mistakes that quietly degrade output quality.
What the system prompt is actually for
In the Claude API, the system parameter is a dedicated field, not a message in the messages array. That distinction matters: Claude treats system-level instructions with higher priority than user turns, and they persist across the entire conversation without being repeated.
Use the system prompt for things that don't change turn to turn:
- Role and persona ("You are a support agent for a logistics company")
- Output format rules (JSON schema, markdown structure, length limits)
- Behavioral constraints (what not to do, how to handle ambiguous requests)
- Domain knowledge or reference data that applies to the whole session
- Tool use policy (when to call a tool vs. answer directly)
Keep anything that changes per request — the actual question, the specific document, dynamic context — in the user message instead. Mixing the two makes prompts harder to maintain and increases the chance Claude treats one-off context as a permanent rule.
Structure it like a spec, not a paragraph
The single biggest improvement most teams can make is switching from a prose paragraph to a structured document with headings and lists. Claude follows structured instructions more consistently than dense paragraphs, especially as the prompt grows past a few hundred words.
You are a billing assistant for Acme SaaS.
## Role
Answer customer questions about invoices, subscriptions, and refunds.
## Rules
- Never disclose internal pricing logic or margins.
- If the user asks about a refund over €500, escalate instead of approving.
- Always respond in the language the user wrote in.
## Output format
Respond in plain text, no markdown headers, max 4 sentences unless
the user explicitly asks for detail.
## Tools
Use `lookup_invoice` only when the user references a specific invoice
number or date. Do not guess invoice numbers.
This structure does three things: it makes the prompt easy to diff and version, it reduces ambiguity about which instruction applies to which situation, and it makes debugging much faster — you can point to the exact line that caused unwanted behavior.
Be explicit about edge cases, not just the happy path
Most system prompt failures happen at the edges: ambiguous user input, missing data, requests outside scope. If you don't specify behavior for these cases, Claude will infer something reasonable, but "reasonable" isn't always what you want.
Instead of:
Help users with their orders.
Write:
Help users with their orders.
- If the order number doesn't exist in the data provided, say so explicitly
and ask the user to double-check it. Do not fabricate order details.
- If the user asks about something unrelated to orders, politely redirect
them to the appropriate channel.
Anywhere you've seen the model do something unexpected in testing, that's a missing instruction, not a model flaw. Add a rule for it and re-test.
Keep it stable across requests, not regenerated each time
System prompts should be static or template-based, not dynamically rewritten per request. If you're building the system prompt string from scratch on every API call, small wording variations can produce inconsistent behavior across users. Define it once as a template with variable substitution for things like the user's name or account tier, and keep the surrounding structure fixed.
const systemPrompt = `You are a support agent for ${companyName}.
## Rules
- Tier: ${userTier}
- Escalate refunds over ${refundThreshold}.
`;
This also makes prompts easier to version and roll back if a change causes regressions.
Don't over-stuff the system prompt with context
A common mistake is treating the system prompt as a dumping ground for reference documents, FAQs, and product data. Long static context belongs there if it's genuinely constant across the whole session — a style guide, a policy document, a set of tool definitions. But per-request data (the current ticket, the specific document being discussed) should go in the user message or be retrieved via tool use, not pasted into the system prompt on every call.
Overloading the system prompt has two costs: it increases latency and token usage on every single request, and it dilutes the instructions that actually matter by burying them in reference material. If you're paying per token through the Claude API or through a wrapper like SubToAPI, this also directly affects your usage metadata and cost per request — worth checking in your dashboard if system prompts have grown large over time.
System prompts and tool use
When Claude has access to tools, the system prompt should describe policy, not mechanics — the tool schema already tells Claude what parameters a tool expects. What the system prompt adds is judgment: when to call a tool, when not to, and what to do with the result.
Use `search_orders` only when the user provides an order number or email.
Never call `issue_refund` without explicit confirmation from the user.
If a tool call fails, tell the user something went wrong — do not
retry silently more than once.
If you're building agentic workflows, this kind of policy layer is often what separates a demo from something safe to run in production. See /docs/tools for tool definition details if you're integrating this through SubToAPI's API.
Version and test your system prompts like code
Treat system prompt changes with the same rigor as code changes: keep them in version control, write a small set of test conversations that exercise your edge cases, and re-run them before deploying a prompt change. A one-line wording change can shift behavior in ways that aren't obvious from reading the diff alone. Teams that skip this step tend to discover regressions from user complaints instead of tests.
If you're calling Claude through SubToAPI, the request format stays the same system field as the native API, so existing prompts port over without rewriting — see /docs/messages for the full request shape, or /docs/quickstart to get an API key running in a few minutes.
questions
Should I put instructions in the system prompt or the first user message? Persistent, session-wide rules belong in the system prompt. Anything specific to the current request — the actual question, a document to analyze — belongs in the user message. This keeps the prompt reusable and the conversation history clean.
How long should a Claude system prompt be? As long as it needs to be to remove ambiguity, but no longer. A few hundred words with clear structure usually outperforms a much longer, unstructured one. If you're pasting large reference documents in, consider whether they should be retrieved via tool use instead of sent on every request.
Do system prompts work the same way across streaming and non-streaming requests? Yes — the system parameter behaves identically regardless of whether you're using streaming responses. See /docs/streaming if you're building a streaming integration and want to confirm request formatting.