In the previous lesson, we learned how to structure messages and provide instructions to the OpenAI Responses API. But consider a user who says, “My favorite language is Python,” and then asks, “What is my favorite language?” If the second API request does not include the first exchange in its context, the model has no reliable way to know the answer. Building a useful conversation means deliberately preserving the information that later requests need. OpenAI conversation memory helps Python applications maintain context across multiple API requests. Without access to earlier messages, a language model cannot reliably answer questions that depend on previous interactions.
This tutorial explains three beginner-friendly ways to maintain conversation context with the OpenAI Python SDK: manually sending history, linking responses with previous_response_id, and using a persistent conversation object.
1. What does conversation memory mean?
For an API application, conversation memory means supplying relevant earlier information when generating the next answer. It does not mean the model permanently remembers every message from every API call. An isolated request only has the context you send or reference through a supported state mechanism.
A conversation might have these turns:
- User: “My favorite programming language is Python.”
- Assistant: “Python is a popular language for beginners.”
- User: “What language did I mention?”
The third turn needs information from the first. The application must preserve that relationship. The three approaches in this lesson differ in where the history is managed, not in whether context matters.
2. Independent requests versus conversation state
Two independent calls to responses.create() do not automatically form a conversation simply because they use the same Python client.
first = client.responses.create(
model="gpt-4.1-mini",
input="My favorite programming language is Python.",
)
second = client.responses.create(
model="gpt-4.1-mini",
input="What is my favorite programming language?",
)
In this example, the second request does not reference the first response or repeat its information. The model might guess, but it has not been given the necessary context. Reusing client keeps the API connection configuration available. It does not make the client a memory store.
3. How Does OpenAI Conversation Memory Work?
flowchart LR
subgraph APP["Python Application"]
U["New user turn"] --> M{"Context strategy"}
H["Local message history"] --> M
R["Previous response ID"] --> M
V["Conversation ID"] --> M
M --> SDK["OpenAI SDK client"]
end
SDK -->|"Request + context reference"| API["OpenAI API"]
API -->|"Response"| SDKThe Python SDK runs inside your application. With manual history, your application sends earlier messages again. With response chaining, it supplies an earlier response ID so the API can use the linked context. With the Conversations API, it refers to a persistent conversation object whose items can be used across responses. The approaches have different storage and control trade-offs.
4. Set up the example project
You need a Python virtual environment, an OpenAI API key, and access to a supported text model. If you completed Blogs 02–05, you can reuse that setup.
Install the packages:
pip install --upgrade openai python-dotenv
pip show openai
In the project root, create .env:
OPENAI_API_KEY=your_api_key_here
5. Approach one: manage message history yourself
The most explicit technique is to keep a list of messages in Python. For each new turn, include the relevant earlier user and assistant messages in the next request.
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
history = [
{
"role": "user",
"content": "My favorite programming language is Python.",
}
]
first = client.responses.create(
model="gpt-4.1-mini",
input=history,
)
history.append({
"role": "assistant",
"content": first.output_text,
})
history.append({
"role": "user",
"content": "What is my favorite programming language?",
})
second = client.responses.create(
model="gpt-4.1-mini",
input=history,
)
print(second.output_text)
The important step is adding the assistant’s answer and the next user message to history. When the second request is sent, the earlier statement about Python is included in its input. This basic text-only example is intentionally simple. More advanced responses can contain reasoning or tool items, so copying only output_text is not a complete history strategy for every model or workflow.
Advantages: You can inspect, edit, summarize, or remove messages before sending them. You also control how history is saved locally.
Trade-offs: Your application must maintain the list correctly. Longer histories increase input size and may increase token usage and cost. An in-memory list disappears when the program ends unless you save it separately.
6. Approach two: use previous_response_id
The Responses API can link a new request to an earlier response by passing its ID in previous_response_id. This removes the need to manually repeat the previous messages in the simple example.
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
first = client.responses.create(
model="gpt-4.1-mini",
input="My favorite programming language is Python.",
)
second = client.responses.create(
model="gpt-4.1-mini",
previous_response_id=first.id,
input="What is my favorite programming language?",
)
print(second.output_text)
Here, first.id identifies the response to continue from. The API can use the earlier response context when producing the second answer. The response must be available under the API’s storage and retention behavior. The examples assume default response storage and do not set store=False.
There are two details worth remembering. First, linking responses does not make all earlier tokens free. Prior context can still contribute to billed input tokens.
Second, instructions from a previous response are not automatically carried into a later call using
previous_response_id. Supply the instructions you still need on the new request.
For example:
second = client.responses.create(
model="gpt-4.1-mini",
previous_response_id=first.id,
instructions="Answer clearly in one short sentence.",
input="What is my favorite programming language?",
)
Store the latest response ID in your application if you want to continue the same chain in later turns. Do not confuse a response ID with a conversation ID.
7. Approach three: use the Conversations API
The Conversations API provides a durable conversation resource that can hold items across requests. It is useful when your application wants to refer to a conversation by its own ID rather than explicitly managing the complete history or a chain of response IDs.
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
conversation = client.conversations.create()
first = client.responses.create(
model="gpt-4.1-mini",
conversation=conversation.id,
input="My favorite programming language is Python.",
)
second = client.responses.create(
model="gpt-4.1-mini",
conversation=conversation.id,
input="What is my favorite programming language?",
)
print(second.output_text)
The same conversation.id is supplied in both requests. The conversation resource keeps the relevant items associated with that conversation.
Your application must still store the conversation ID if it wants to find the same conversation later. Avoid mixing conversation and previous_response_id in a single Responses API request because these parameters cannot be used together.
A durable conversation resource does not mean unlimited context. Models still have context-window limits, and applications must plan for long histories, privacy, retention, and costs.
8. Compare the approaches
| Approach | Who manages the history? | Best for |
|---|---|---|
| Manual message history | Your Python application | Learning, explicit control, custom trimming |
previous_response_id | API-linked response chain, referenced by your application | Simple follow-up requests |
| Conversations API | Persistent conversation resource, referenced by your application | Reusing a conversation across requests or sessions |
9. Complete two-turn example with response chaining
This example keeps the setup minimal and shows how the response ID changes as the conversation progresses.
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
first_response = client.responses.create(
model="gpt-4.1-mini",
instructions="Answer in simple, beginner-friendly language.",
input="My favorite programming language is Python.",
)
print("First response:")
print(first_response.output_text)
second_response = client.responses.create(
model="gpt-4.1-mini",
previous_response_id=first_response.id,
instructions="Answer in simple, beginner-friendly language.",
input="Which programming language did I mention?",
)
print("Second response:")
print(second_response.output_text)
Save the file as main.py and run python main.py in the active virtual environment. Both requests use the same model, and the second explicitly references the first. We repeat the instruction because a previous request’s instructions are not implicitly carried forward. The actual generated wording and response IDs vary by execution.
10. Common mistakes and troubleshooting
| Problem | What to check |
|---|---|
| Follow-up answer ignores earlier information | Verify that you sent history, a valid previous response ID, or a conversation ID |
| Reusing the same Python client does not preserve context | The client is not a conversation store |
previous_response_id fails | Check the ID, access, and whether the referenced response is available |
| Assistant changes style unexpectedly | Repeat required instructions on subsequent chained requests |
| Token usage increases over time | Previous context can still count toward billed input tokens |
| History becomes too long | Plan trimming or summarization without removing essential facts |
| A conversation appears lost after restarting Python | Save the appropriate history or conversation identifier securely |
| API rejects context parameters | Do not pass conversation and previous_response_id together |
Do not store private conversation content unnecessarily. If you persist histories or conversation IDs, apply appropriate access controls and retention practices.
11. Does Conversation Memory Work with Local LLMs?
Yes. Conversation memory works with local language models, including Qwen3-Coder, because the underlying idea is the same: the model needs the relevant earlier messages in its current input context. Memory management is primarily an application or model-serving feature, not a special capability reserved for frontier models.
The important difference is where conversation state lives. With OpenAI’s hosted API, previous_response_id and the Conversations API can refer to state managed by OpenAI’s backend. The model does not independently fetch a database record. The API service resolves the reference and prepares the context for inference.
For a local model, support depends on the serving software. An Ollama, LM Studio, vLLM, or custom server may expose different APIs and different state-management capabilities. Do not assume an OpenAI-compatible endpoint automatically implements OpenAI’s stateful Responses API parameters. Manual history is the most portable technique because your application sends the earlier messages explicitly.
Example: manual history with a local Qwen3-Coder server
The following example assumes a locally running Ollama server that exposes an OpenAI-compatible Chat Completions endpoint. Replace the model name with the exact identifier installed on your computer. The example uses a placeholder local API key because the OpenAI Python SDK expects a value.
from openai import OpenAI
client = OpenAI(
base_url="<http://localhost:11434/v1>",
api_key="ollama",
)
history = [
{
"role": "user",
"content": "My favorite programming language is Python.",
}
]
first = client.chat.completions.create(
model="YOUR_LOCAL_QWEN_MODEL_NAME",
messages=history,
)
history.append({
"role": "assistant",
"content": first.choices[0].message.content,
})
history.append({
"role": "user",
"content": "What is my favorite programming language?",
})
second = client.chat.completions.create(
model="YOUR_LOCAL_QWEN_MODEL_NAME",
messages=history,
)
print(second.choices[0].message.content)
Notice that this local example uses client.chat.completions.create() rather than the hosted Responses API. This is a compatibility example, not a recommendation to change the main course from the Responses API. No request is sent to OpenAI when the client is configured to use the local server address shown above.
| Question | OpenAI hosted API | Local model server |
|---|---|---|
| Can the model use earlier messages? | Yes, when context is supplied | Yes, when context is supplied |
| Can the application resend history manually? | Yes | Yes |
| Can a response ID refer to stored context? | Supported through the Responses API | Only if the chosen server implements it |
| Can a conversation ID refer to persistent state? | Supported through the Conversations API | Only if the chosen server implements it |
| Does the model permanently learn the conversation? | No | No |
Key takeaway: The same model-context principle applies to hosted and local LLMs. What changes is whether your application must resend history or can ask the serving backend to retrieve it. In either case, the model processes context available for the current generation, subject to context-window limits. Local-server API support should be verified against the version and serving stack you actually use.
12. Summary and next lesson
Conversation memory is about providing relevant context for each new response. You can manage messages yourself, link responses using previous_response_id, or associate requests with a persistent conversation. Each technique helps the model respond to follow-up questions, but none should be confused with unlimited or automatic personal memory.
In Blog 07 — Streaming OpenAI Responses in Python, we’ll learn how to display generated text incrementally instead of waiting for a complete response.
References and related lessons
- OpenAI Conversation State Guide
- OpenAI Responses API Reference
- OpenAI Conversations API Reference
- Official OpenAI Python SDK
- Blog 05 — Messages, Roles, and Instructions