OpenAI Assistants API Guide: Build, Use, and Migrate AI Agents

openai-assistants-api
ai-agents
migration-guide

The OpenAI Assistants API lets developers create purpose‑driven AI agents that manage conversation state, run tools, and retrieve knowledge without writing boilerplate for each turn. Although the API is marked for deprecation in August 2026, it remains a powerful way to prototype and run agents today, and understanding it eases the transition to the newer Responses API. This guide walks through the API’s fundamentals, shows a realistic implementation, covers pricing and best practices, and explains how to migrate when the time comes.

Core Concepts

At its heart, the Assistants API revolves around four objects that work together:

  • Assistant – A reusable agent definition that bundles a model, instructions (a system prompt), and a list of enabled tools. You create it once and reuse it across many conversations.
  • Thread – A persistent conversation container. Messages are appended automatically, and OpenAI truncates older content to fit the model’s context window, so you never manually manage token limits.
  • Message – A single user or assistant utterance inside a thread. It can contain plain text, images, or file attachments.
  • Run – The activation of an assistant on a thread. A run progresses through queued → in_progress → (requires_action if a tool is needed) → completed (or failed/expired/cancelled). While a run is in progress, the thread is locked, preventing new messages or runs.

This stateful design removes the need to resend the whole history with each request, unlike the stateless Chat Completions API.

Built‑in Tools

The API ships with three first‑party tools that extend the model’s capabilities:

  1. Code Interpreter – Provides a sandboxed Python environment. The assistant can write and execute code, analyze uploaded CSV/Excel files, generate charts, and return files for download. Pricing is per session (default $0.03 for a 20‑minute session with a 1 GB container).
  2. File Search – Implements managed Retrieval‑Augmented Generation. You upload documents (PDF, DOCX, TXT, etc.) with the purpose “assistants”. OpenAI chunks, embeds, and stores them in a vector store. When the assistant needs information, it queries the store and returns relevant chunks. Storage costs $0.10 / GB / day after the first free GB; each tool call costs $2.50 per 1 000 calls.
  3. Function Calling – Lets you define custom tools you write yourself. You describe a function with a JSON schema (name, description, parameters). When the model decides it needs the function, the run enters a requires_action state. Your backend executes the function and submits the result via submitToolOutputs, after which the run resumes.

You can enable any combination of these tools per assistant, up to 128 total.

Step‑by‑Step Implementation (Python)

Below is a concise, end‑to‑end example that creates an assistant for question‑answering over PDF files using File Search.

from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 1️⃣ Upload the knowledge file
with open("manual.pdf", "rb") as f:
    file_obj = client.files.create(file=f, purpose="assistants")
file_id = file_obj.id

# 2️⃣ Create the assistant
assistant = client.beta.assistants.create(
    name="Knowledge Bot",
    instructions="Answer questions using only the provided documentation.",
    model="gpt-4o",
    tools=[{"type": "file_search"}],
    tool_resources={"file_search": {"vector_store_ids": []}},  # will attach next
)

# 3️⃣ Attach the file to a vector store and link it to the assistant
vector_store = client.beta.vector_stores.create(name="Manual Store")
client.beta.vector_stores.files.create(
    vector_store_id=vector_store.id,
    file_id=file_id,
)
client.beta.assistants.update(
    assistant_id=assistant.id,
    tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}),
)

# 4️⃣ Start a conversation thread
thread = client.beta.threads.create()

# 5️⃣ Add a user message
client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="What is the warranty period for the device?",
)

# 6️⃣ Run the assistant and poll for completion
run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
)

while True:
    run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
    if run.status == "completed":
        break
    elif run.status in {"queued", "in_progress", "requires_action"}:
        # In a real app you might handle requires_action for custom functions
        time.sleep(1)
    else:
        raise RuntimeError(f"Run failed: {run.status}")

# 7️⃣ Retrieve the assistant’s reply
messages = client.beta.threads.messages.list(thread_id=thread.id)
answer = messages.data[0].content[0].text.value
print(answer)

The same logic applies when you swap file_search for code_interpreter or add custom functions to the tools list. The key is to create the assistant once, reuse the thread for a conversation, and poll the run until it finishes.

Pricing and Limits

Understanding cost helps avoid surprise bills:

ComponentCharge
Model tokensSame as Chat Completions (e.g., GPT‑4o: $2.50 / 1M input, $10.00 / 1M output)
Code Interpreter$0.03 per active session (up to 1 hour)
File Search storage$0.10 / GB / day after the first free GB
File Search tool calls$2.50 per 1 000 calls
Function callingNo extra fee; you pay only for the model tokens used in the run

Limits worth noting:

  • File upload: max 512 MB or 5 M tokens per file.
  • Vector store: up to 10 000 files (or 100 000 000 for stores created after Nov 2025).
  • Thread: 100 000 messages max; automatic truncation when the context window fills.
  • No organization‑wide file storage cap (though each project defaults to 2.5 TB; you can request an increase).

Best Practices

  • Define clear instructions – The assistant’s behavior hinges on the prompt you give. Be explicit about tone, scope, and which tools to use.
  • Curate your knowledge base – Upload only relevant documents; noisy or duplicate files degrade retrieval quality and increase storage cost.
  • Handle requires_action gracefully – For custom functions, always submit a tool output (success or error) to prevent the run from hanging indefinitely.
  • Monitor usage – Enable logging of token consumption and tool calls; set alerts for abnormal spikes.
  • Version your assistants – When you update instructions or tools, create a new assistant ID rather than mutating the old one, preserving reproducibility.
  • Respect privacy – Do not send sensitive personal data unless you have a compliant retention and deletion strategy; you can delete files and assistants via the API at any time.

Why the Assistants API Is Being Deprecated

OpenAI announced that the Assistants API will be sunset on August 26 2026. The driver is the arrival of reasoning models (GPT‑5 family) that generate hidden chain‑of‑thought tokens. The Assistants API’s stateless‑per‑run design cannot preserve those internal reasoning steps across turns, whereas the new Responses API keeps the model’s reasoning state in server‑side objects, delivering better multi‑turn performance.

In addition, the Responses API simplifies the programming model:

Assistants APIResponses API EquivalentWhat changes
AssistantPrompt (dashboard‑only)No programmatic creation; you version prompts in the UI.
ThreadConversationStores richer “items” (tool calls, outputs) not just raw messages.
RunResponseSynchronous by default; optional background mode for long tasks.
Run StepItemUnified representation of messages, tool calls, and tool outputs.

Because prompts cannot be created via API, any code that dynamically spins up assistants per tenant or per use case must be re‑architected—typically by storing prompt IDs in source control and referencing them at runtime.

Migration Path: Assistants → Responses + Conversations

OpenAI’s official recommendation is to move to the Responses API for generation and the Conversations API for persistent state. The migration involves four high‑level steps:

  1. Inventory – List all assistants, their instructions, models, tools, and attached files. Note thread volumes and vector store usage.
  2. Create Prompts – In the OpenAI dashboard, turn each important assistant into a versioned prompt. Save the prompt IDs alongside your code.
  3. Replace Threads with Conversations – For each conversation, create a Conversation object (or use previous_response_id chaining if you prefer stateless handling). Pass the conversation ID to client.responses.create.
  4. Adjust Tool Calls – Built‑in tools work similarly, but you now pass vector_store_ids directly in the file_search tool configuration, and Code Interpreter is priced per container session. Function calling remains, but you must inspect the output array for tool_call and tool_output items and link them via call_id.

Cost Implications

Migration can affect your bill:

  • File Search storage stays the same, but each tool call is now $2.50/1 000 calls (unchanged).
  • Responses themselves are free; you pay only for model tokens and tool usage.
  • State carrying – If you opt for manual history or previous_response_id, you resend prior turns on every request, which multiplies input‑token usage. Using a Conversation object avoids that extra token cost for the stored history, though you still pay for new tokens each turn.
  • Code Interpreter pricing shifts to a per‑container session model (e.g., $0.03 for a 1 GB container, scaling up with size).

Run a shadow test: send the same traffic to both Assistants and Responses endpoints, compare token counts, and adjust your budget accordingly.

Alternatives and Wire‑Compatible Options

If you need more time before a full rewrite, a wire‑compatible shim can keep your existing Assistants‑style calls working while pointing to a different backend. Services like Ragwalla or open‑source projects (e.g., DataStax’s astra‑assistants‑api) implement the Assistants API contract over their own infrastructure. This lets you preserve the assistants.beta.* call pattern, but you lose access to OpenAI’s newest reasoning model improvements and may see slight differences in tool behavior (e.g., chunking, streaming).

For simple Q&A or single‑turn tasks, staying with the stateless Chat Completions API remains viable and cheaper, as it avoids the overhead of threads and runs. For complex agentic workflows that require multi‑step tool use, the Responses/Conversations stack is the forward‑looking path.

Conclusion

The OpenAI Assistants API has lowered the barrier to building stateful, tool‑enabled AI agents. By grasping its core pieces—assistants, threads, messages, and runs—you can quickly spin up prototypes that handle file retrieval, code execution, or custom integrations. While the API is slated for removal in August 2026, the concepts you learn map directly onto the Responses API and Conversations API, making migration a manageable engineering effort rather than a rewrite‑from‑scratch effort.

Start by auditing your current usage, version your assistants as prompts, prototype a conversation‑based flow in the Responses API, and monitor tool costs as you go. With those steps, you’ll transition smoothly to the next generation of OpenAI’s agent platform while keeping your AI features running reliably for users.

Share this post:
OpenAI Assistants API Guide: Build, Use, and Migrate AI Agents