The single most important thing to understand about the Claude Messages API: it has no memory. Every API request is completely independent. There is no session, no conversation ID, no server-side state.
What this means in practice
When you send your 4th message in a conversation, you don’t just send that 4th message. You send all four — the complete conversation history, every user and assistant message from the beginning. The API processes this array as if it’s seeing the conversation for the first time, because it is.
The request body has three key parts:
model— which Claude model to usemessages— an array of the full conversation history (user and assistant turns)system— an optional top-level parameter for the system prompt
Notice that system is not inside the messages array. It sits at the top level of the request, separate from the conversation flow. This is a deliberate design choice: the system prompt provides persistent behavioral framing (role, tone, constraints) that contextualizes everything, while messages capture the back-and-forth dialogue.
The system prompt is optional
You can send a perfectly valid request without a system prompt. Many applications don’t need one. The model, messages, and max_tokens are the only required parameters.
Why statelessness matters
Every additional turn makes requests larger and more expensive. By turn 50, your input includes 49 prior messages of accumulated history. This isn’t a bug — it’s the fundamental tradeoff of a stateless API. You get simplicity (no session management, no expiration, no server-side state to corrupt) at the cost of growing request sizes.
If a multi-turn conversation suddenly loses context — the model acts like it’s never seen earlier messages — the cause is almost certainly that your code failed to include the full history in the request. The server didn’t “forget” anything. It never knew.
One-liner: The API is stateless — every request must carry the complete conversation history, and the system prompt lives at the top level, not inside messages.