OpenAI Assistants API Code Interpreter: A Practical Guide
The Code Interpreter tool turns an assistant into a lightweight Python sandbox. Instead of asking the model to guess a numeric answer, you let it write, run, and iterate on real code until it gets a result that works. This capability is especially handy for data analysis, file conversion, chart generation, and any task where trial‑and‑error beats a one‑shot guess.
Below you’ll find a step‑by‑step walkthrough, concrete code snippets, and the practical considerations you need to know before putting the interpreter into production.
How the Code Interpreter fits into an Assistant
When you create an assistant you declare which tools it may use. Adding { "type": "code_interpreter" } tells the Assistants API to spin up a sandboxed Python environment whenever the model decides it needs to execute code. The flow looks like this:
- Assistant receives a user message – the model reads the instruction and decides whether to answer directly, call a function, or run code.
- If code is chosen – the model generates a Python snippet, sends it to the interpreter, and waits for the output.
- On error – the model can read the traceback, adjust the code, and try again automatically. This self‑correction loop is what makes the interpreter more reliable than a naive code‑generation prompt.
- Result returned – any printed output, generated files, or charts are passed back to the model, which then incorporates them into its final reply.
The interpreter is stateless aside from the files you explicitly mount. Each time a new thread first invokes the tool, a session starts and stays alive for one hour of activity. Subsequent calls in the same thread reuse that session, avoiding extra charges.
Setting up the assistant and thread
The following Python example shows the minimal calls needed to create an assistant equipped with the Code Interpreter, upload a file, and run a simple query.
from openai import AzureOpenAI # or openai.OpenAI for the public API
client = AzureOpenAI(
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-05-01-preview",
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
)
# 1️⃣ Create the assistant
assistant = client.beta.assistants.create(
name="Data‑analysis helper",
instructions="You are a helpful data analyst. Write and run Python code to answer questions.",
model="gpt-4o", # or any model that supports the tool
tools=[{"type": "code_interpreter"}],
)
# 2️⃣ Upload a file the assistant may need (CSV, Excel, etc.)
with open("sample_data.csv", "rb") as f:
file_obj = client.files.create(file=f, purpose="assistants")
# 3️⃣ Attach the file to the assistant so it’s available in every thread
assistant = client.beta.assistants.update(
assistant_id=assistant.id,
tool_resources={
"code_interpreter": {"file_ids": [file_obj.id]}
},
)
# 4️⃣ Start a conversation thread
thread = client.beta.threads.create()
# 5️⃣ Add a user message that references the uploaded file
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="What is the average value in the 'price' column of the uploaded CSV?",
file_ids=[file_obj.id], # optional: limit the file to this message only
)
# 6️⃣ Run the assistant and wait for completion
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id,
)
# Simple polling loop – in production you might use streaming or webhook
while run.status in ("queued", "in_progress"):
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
# 7️⃣ Retrieve the assistant’s final reply
messages = client.beta.threads.messages.list(thread_id=thread.id, order="desc")
print(messages.data[0].content[0].text.value)
What each block does
| Step | Purpose |
|---|---|
| Assistant creation | Declares the model, instructions, and the Code Interpreter tool. |
| File upload | Sends a file to the OpenAI Files API with purpose="assistants". The file becomes mountable inside the sandbox at /mnt/data/. |
| Tool resources | Binds the uploaded file(s) to the assistant (or to a specific thread) so the interpreter can see them. |
| Thread & message | Holds the conversation; attaching file_ids limits visibility to that turn if you don’t want the file globally available. |
| Run & poll | Executes the assistant; the interpreter runs automatically when the model decides it’s needed. |
| Result extraction | Retrieves the final text (or file references) generated by the assistant. |
Passing files to a specific thread only
Sometimes you want a file to be private to a single conversation, perhaps because it contains sensitive data. The pattern is simple: instead of registering the file at the assistant level, pass it directly in the message creation call:
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="Please summarize the attached log file.",
file_ids=[private_file.id],
)
When the model generates code, the sandbox will mount only the files listed in that message’s file_ids. This isolation prevents accidental leakage across threads.
Downloading files generated by the interpreter
The assistant can return images, CSV files, or any other artifact it creates. These appear as image_file or file annotations in the message content. To fetch them:
# Assume `msg` is the assistant message containing an image_file block
image_file_id = msg.content[0].image_file.file_id
image_data = client.files.content(image_file_id) # returns a stream
with open("output_chart.png", "wb") as f:
f.write(image_data.read())
The same approach works for any file type—just replace the wb mode and extension as needed.
Pricing and session management
The Code Interpreter adds a flat fee on top of the usual token costs:
- $0.03 per session.
- A session starts the first time the tool is invoked in a thread and stays alive for one hour of activity (not wall‑clock time).
- Additional calls within that hour reuse the same session at no extra charge.
- If you spin up a new thread for every user message, each message that uses the interpreter triggers a new session, which can become expensive quickly.
Cost‑effective patterns
| Pattern | Description | When to use |
|---|---|---|
| Long‑lived thread per user | Keep the same thread open for the duration of a user’s session (e.g., a chat widget). The first code call pays $0.03; subsequent calls are free. | Interactive apps where users ask multiple related questions. |
| Short‑lived thread per request | Create and discard a thread after each message. Simpler to reason about but costly if the interpreter is used often. | Batch jobs or infrequent, isolated queries. |
| Pool of reused threads | Maintain a small pool of threads and rotate them among users, resetting the conversation state as needed. | Middle ground when you need some isolation but want to limit session starts. |
Always monitor the usage field on the run object; it includes token counts that usually dominate the bill unless you are launching many short sessions.
What the interpreter can (and cannot) do
Strengths
- Ad‑hoc data analysis – Load a CSV or Excel file, run
pandas, compute aggregates, filter, group‑by, and produce charts withmatplotliborseaborn. - File format conversion – Turn PDFs into text, images into CSV, or Excel into JSON using the pre‑installed libraries (
PyPDF2,openpyxl,Pillow). - Chart generation – Inline PNG/JPEG outputs that the assistant can return as file attachments.
- Precise math – Numerical integration, statistical tests, regression, and any computation where the model would otherwise hallucinate.
- Schema/validation checks – Run
jsonschemaor custom validation logic inside the sandbox before returning structured output.
Limitations
- No internet – The sandbox blocks outbound HTTP requests. If you need live APIs, pre‑download the data and upload it as a file, or pair the interpreter with a custom function tool that performs the request on your side.
- No GPU – Only CPU‑based libraries are available. For deep‑learning inference or model training you must bring your own sandbox.
- No persistent storage – Files disappear when the session ends (after one hour of inactivity). For longer‑term state, store results in a database or object storage yourself.
- Limited library set – You cannot
pip installarbitrary packages. The environment includes a curated stack (pandas, numpy, matplotlib, scipy, scikit‑learn, openpyxl, PyPDF2, Pillow, etc.). If you need something outside that list, consider a DIY sandbox (Modal, E2B, Daytona) and expose it via a function tool.
Combining Code Interpreter with function calling
A common pattern is to let the assistant write code that needs external data, then have that code call a function you expose. The assistant generates the Python snippet, the interpreter runs it, and when it hits a function_call the orchestration pauses, executes your custom code, and feeds the result back.
assistant = client.beta.assistants.create(
name="Web‑augmented analyst",
instructions="You can write Python to analyze data. If you need live data, call the `fetch_url` function.",
model="gpt-4o",
tools=[
{"type": "code_interpreter"},
{
"type": "function",
"function": {
"name": "fetch_url",
"description": "Retrieve the contents of a URL and return it as text.",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string"}
},
"required": ["url"]
}
}
}
],
)
Your fetch_url function might simply use requests.get(url).text. The assistant sees the returned text as ordinary stdout from its generated code, enabling seamless integration of live APIs without leaving the interpreter’s safety net.
Practical tips for reliable execution
- Be explicit in the instruction – Tell the model to plan first, check errors, and re‑run if needed. The more guidance, the less likely it will stall on a syntax error.
- Validate file paths – The sandbox mounts files under
/mnt/data/. When you generate code, refer to files with that absolute path or rely on the assistant’s automatic path resolution (it will prepend/mnt/data/for you). - Limit output size – Large prints or huge dataframes can balloon the response. Use
head()ordescribe()to keep the output readable, and optionally write large results to a file you then download. - Handle exceptions gracefully – If the model’s code raises an exception, the interpreter returns the traceback. Your instruction should encourage the model to read that traceback, fix the bug, and retry.
- Clean up after yourself – Delete assistants, threads, and uploaded files when they’re done to avoid lingering storage charges. The cleanup snippet from the OneUptime post is a good starting point.
- Monitor sessions – If you notice the per‑session fee adding up, consider consolidating work into fewer, longer‑lived threads.
Full example: analyzing user‑uploaded sales data
Below is a self‑contained script that demonstrates the end‑to‑end flow: upload a CSV, ask for a summary, generate a bar chart, and download the image.
from openai import AzureOpenAI
client = AzureOpenAI(
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-05-01-preview",
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
)
# ---- Assistant -------------------------------------------------
assistant = client.beta.assistants.create(
name="Sales analyst",
instructions="You are a data analyst. Load the CSV, compute summary statistics, and create a matplotlib bar chart of monthly sales.",
model="gpt-4o",
tools=[{"type": "code_interpreter"}],
)
# ---- Upload CSV ------------------------------------------------
with open("sales.csv", "rb") as f:
sales_file = client.files.create(file=f, purpose="assistants")
assistant = client.beta.assistants.update(
assistant_id=assistant.id,
tool_resources={"code_interpreter": {"file_ids": [sales_file.id]}},
)
# ---- Thread & user request --------------------------------------
thread = client.beta.threads.create()
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content=(
"Please load the uploaded sales.csv, compute total sales per month, "
"and produce a bar chart saved as 'monthly_sales.png'."
),
file_ids=[sales_file.id],
)
# ---- Run assistant ---------------------------------------------
run = client.beta.threads.runs.create(thread_id=thread.id, assistant_id=assistant.id)
while run.status in ("queued", "in_progress"):
time.sleep(2)
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
# ---- Retrieve response and download chart -----------------------
messages = client.beta.threads.messages.list(thread_id=thread.id, order="desc")
for block in messages.data[0].content:
if hasattr(block, "image_file"):
file_id = block.image_file.file_id
img = client.files.content(file_id)
with open("monthly_sales.png", "wb") as f:
f.write(img.read())
print("Chart saved to monthly_sales.png")
elif hasattr(block, "text"):
print("Assistant:", block.text.value)
Running this script yields a textual summary plus a PNG file you can display or store wherever you need.
When to reach for a DIY sandbox
If your use case demands any of the following, consider bringing your own Python environment and exposing it via a function tool:
- Internet access for live APIs or web scraping.
- GPU acceleration for model inference or training.
- Custom libraries not present in the interpreter’s pinned set (e.g.,
torch,transformers,spyder). - Persistent state across multiple hours or days.
- Fine‑grained control over the container (environment variables, security policies, etc.).
Platforms like Modal, E2B, or Daytona let you spin up a secure container, run arbitrary code, and stream back stdout/stderr. You then wrap that capability in a function definition that the assistant can call just like fetch_url in the earlier example. The trade‑off is you must implement your own error‑retry loop; the interpreter’s built‑in self‑correction is lost unless you code it yourself.
Wrap‑up
The OpenAI Assistants API Code Interpreter gives you a managed Python sandbox that the agent can use to write, run, and refine code on the fly. It shines for data‑centric tasks—especially when you need the model to correct its own mistakes through iterative execution. By understanding how sessions are billed, how to mount and retrieve files, and how to combine the tool with custom functions, you can build assistants that are both powerful and cost‑conscious.
Whether you’re generating charts from a user‑provided spreadsheet, converting file formats, or performing precise numerical analysis, the interpreter removes the guesswork and lets the model rely on actual computation. Keep the best practices in mind—clear instructions, proper file handling, session reuse, and diligent cleanup—and you’ll have a reliable foundation for AI‑driven data work.
