System Prompting

Beginner3 min

The Messages API takes the system prompt as its own parameter rather than a first message. Putting the date or a user's name in it stops the prefix caching, so you pay the write premium and never collect the read discount.

#prompting

The system prompt is a parameter, not a message

In the Messages API there is no system role. The reference is blunt about it: "there is no "system" role for input messages in the Messages API." The system prompt is a top-level parameter sitting beside messages rather than inside it.

await client.messages.create({
  model: 'claude-sonnet-5',
  max_tokens: 1024,
  system: 'You are a support assistant. Answer only from the supplied policy.',
  messages: [{ role: 'user', content: 'Can I return this after 40 days?' }],
})

Other providers model the same idea as a message with a different role. The distinction matters less for how the model behaves than for where the text sits in your request, which turns out to be the practical half.

What belongs in it, and what belongs in the turn

Sort by lifetime. If a sentence is true of every request your feature makes, it belongs in the system prompt. If it changes per request, it belongs in the user turn.

System prompt. Who the assistant is, what it is for, the format it must answer in, the rules it must follow, and the examples that show what good looks like. All of that is written once and reused.

User turn. The question, and the material to answer it from. A retrieved document belongs here even though it is instruction-adjacent, because it changes every time. Providing context covers how to lay that material out once it is in the turn.

The awkward cases are instructions that feel durable but are not. "Answer in Spanish" is durable if your product is Spanish and per-request if the user picks a language. Putting a per-request value in the system prompt works, and it costs you the thing the next section is about.

Why the split shows up on the bill

A stable system prompt is the natural thing to cache, and caching is priced specifically enough to do the arithmetic.

On Anthropic's published rates, checked 2026-09-16, a cache read is billed at 0.1x the base input price. The documentation puts the break-even plainly: a cache hit "costs 10% of the standard input price, which means caching pays off after one cache read for the 5-minute duration (1.25x write), or after two cache reads for the 1-hour duration (2x write)."

That only works if the cached prefix is identical between requests. A system prompt holding a user's name, the current date, or a per-request language setting is a different prefix every time, so it is written to cache and never read from it. You pay the write premium and collect none of the discount.

The rule that falls out: keep the system prompt byte-identical across requests, and put anything that varies into the turn, even when it reads like an instruction. Prompt caching covers the mechanism in full.

What a system prompt does not enforce

A system prompt is more durable than an instruction buried in a user message, and it is still text the model reads.

It will not stop a model following instructions that arrive inside retrieved content, and it will not make a refund policy binding. A rule you need enforced belongs in code that runs after the model responds, which is the same conclusion MCP server reaches about tool results and what is an AI engineer reaches about refund limits.

The useful way to hold it: the system prompt sets defaults and shapes behavior. Constraints that have to hold every time go in code, and prompt injection covers what happens when you rely on the prompt instead.

Further reading

Knowledge check

Question 1 of 3

You are porting code that puts the system prompt as the first entry in the message array. What changes in the Messages API?

Sign in to save your progress and pick up where you left off.