← Blog

Claude API Multi-Turn Conversation Handling Guide

2026-11-04 · 5 min read · SubToAPI Team

Multi-turn conversation handling with the Claude API means managing the growing list of messages exchanged between a user and the model across a session, and sending the right subset of that history back with every request. Claude's API is stateless — it does not remember previous calls — so your application is responsible for storing conversation history and replaying it correctly on each turn.

The core mechanic is simple: every request includes a messages array with alternating user and assistant roles, and you append the latest exchange before sending the next request. The hard parts are everything around that: where you store history, how you trim it when it gets too long, how you handle system prompts and tool calls across turns, and how you keep latency and cost under control as conversations grow. This article covers the practical patterns for doing that correctly.

The basic message array pattern

A multi-turn conversation is just an ordered list of messages you resend each time:

{
  "model": "claude-opus-4",
  "max_tokens": 1024,
  "messages": [
    { "role": "user", "content": "I'm building a CLI tool in Go. Any gotchas with flag parsing?" },
    { "role": "assistant", "content": "The standard library's flag package doesn't support..." },
    { "role": "user", "content": "What about subcommands then?" }
  ]
}

The model only sees what's in this array. There is no hidden session on Anthropic's side. That means your app needs to:

  1. Persist each turn (user message, assistant reply) somewhere — a database row, a Redis key, a local file for prototyping.
  2. Reconstruct the messages array in order before each new call.
  3. Append the new user message, send the request, then append the assistant's reply to storage once it comes back.

If you skip step 3 for even one turn, the model loses continuity and starts answering as if the prior exchange never happened.

Where to store conversation state

For anything beyond a demo, store conversations server-side, not in the browser. A typical schema:

This lets you paginate long conversations, run analytics on turn counts, and rebuild context for a conversation even if the client reloads. Client-side storage (localStorage, in-memory state) is fine for single-session tools but breaks down the moment you need multi-device access or audit logs.

Managing context window limits

Claude models have large but finite context windows. As conversations grow, you'll eventually need a truncation strategy rather than sending the full history forever. Common approaches:

A practical middle ground for most chat apps is a hybrid: keep the last 10–20 turns verbatim, and summarize anything older into a single system or user message that gets prepended.

function buildMessages(history, summary) {
  const recent = history.slice(-20);
  const messages = [];
  if (summary) {
    messages.push({ role: "user", content: `Context summary: ${summary}` });
  }
  return messages.concat(recent);
}

Trim on token count, not message count, when precision matters — a long code block in one turn can dwarf ten short chat messages.

Keeping the system prompt stable

The system parameter sits outside the messages array and should generally stay constant across a conversation's turns — it defines the assistant's role, tone, and constraints. Changing it mid-conversation can produce jarring behavior shifts. If you need to adjust instructions dynamically (e.g., switching modes), it's usually cleaner to inject that as a user-role message ("From now on, respond only in bullet points") rather than rewriting the system prompt, since the model handles in-conversation instructions more predictably than silently swapped system context.

Handling tool use across turns

If your conversation involves tool calls, multi-turn handling gets an extra layer: the assistant's tool_use content block and your subsequent tool_result message both need to be appended to history in order, exactly as returned, before the next user turn. Skipping or reordering these breaks the model's ability to reference what the tool returned. See /docs/tools for the structure of tool-call messages if you're building this.

Streaming and multi-turn state

When streaming responses, don't commit the assistant message to your conversation store until the stream completes (or fails). A common bug is persisting partial content from a dropped connection, which then gets replayed as "what the assistant said" in the next turn — producing confusing, truncated context. Buffer the full response, then write it once. Details on handling stream events are in /docs/streaming.

Multi-turn handling with SubToAPI

If you're exposing Claude through SubToAPI (https://subtoapi.app) rather than calling Anthropic directly, the message-array pattern is identical — SubToAPI proxies the same messages format, so your conversation storage and truncation logic don't change. What you get on top is a stable sub_live_... key per application, per-key usage metadata so you can see token consumption per conversation or per team seat, and streaming support without touching your own backend infrastructure. A typical call looks like:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-4",
    "max_tokens": 1024,
    "messages": [
      { "role": "user", "content": "Summarize our last three turns." }
    ]
  }'

You still own the conversation store; SubToAPI just handles the request layer. See /docs/messages for the full request shape and /docs/quickstart to get a key provisioned. Pricing starts at Solo €9/month, with Team (€19/seat) and Scale (€49/seat) plans if multiple people or services need their own keys under one dashboard — see /pricing.

Practical checklist

Questions

Does the Claude API remember previous messages automatically? No. The API is stateless — every request must include the full relevant message history in the messages array. There is no server-side session memory.

How do I handle conversations that exceed the context window? Use a truncation strategy: keep recent turns verbatim and either drop or summarize older ones before sending the request, tracking token counts rather than message counts for accuracy.

Should the system prompt change between turns in the same conversation? Generally no. Keep it stable for predictable behavior, and handle mid-conversation instruction changes as regular user messages instead.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →