Search nomadLab

The Assistants API Stops Answering on 26 August

Four days left, and only one part of this migration has a deadline you cannot move: your threads become unreadable the moment the endpoints go. The code can wait a week. The export cannot.

Updated

On 26 August 2026, OpenAI removes the Assistants API endpoints. Not deprecated and still answering. Removed. /v1/assistants, /v1/threads, /v1/runs, every path your code still calls.

I first wrote this in July, when there was a month of runway. There are four days. That changes which half of the work matters. The code migration is a week of ordinary engineering and you can do it in September if you have to, because your users will simply see errors until you ship. The export is different. After the 26th, the thread contents are unreachable through the API whatever OpenAI’s internal retention says, and there is no tool coming to get them out for you.

So if you read one section of this, read the export one, and run it today.

Nobody is exporting your threads

OpenAI is explicit about this in the migration guide: “We will not provide an automated tool for migrating Threads to Conversations.” The suggested path is to read messages out of each thread and write them into a conversation yourself.

msgs = client.beta.threads.messages.list(thread_id=old_thread_id, order="asc")
items = [{"role": m.role, "content": m.content[0].text.value} for m in msgs.data]
conv = client.conversations.create(items=items)

That version flattens everything. Tool calls, file citations, annotations, run metadata: none of it survives a content[0].text.value extraction. Which is fine for most chat products, where all you need is enough context for the model to keep going. It is not fine if anyone might ask you for the record later, and the distinction is not one you get to revisit after Wednesday.

With four days left I would not agonize over the choice. Dump the raw API responses to disk first, unparsed, one JSON file per thread. That takes an afternoon to write and it preserves the option to decide properly in September. Parsing cold storage is a normal Tuesday. Recovering from an endpoint that returns 404 is not.

Budget for rate limits if you have tens of thousands of threads. Listing messages thread by thread is a lot of requests, and the window does not stretch to accommodate your retry backoff.

What replaces what

The replacement is two APIs, not one. Responses handles execution, Conversations handles state. Assistants folded both jobs into one object graph, which is why this feels less like a rename and more like taking apart something that used to arrive assembled.

AssistantsReplacementWhat changed
AssistantPromptModel, instructions, tools, but managed through the dashboard
ThreadConversationA stream of items rather than only messages
RunResponseNo polling, you get output items back directly
Run stepItemGeneralized: messages, tool calls, and outputs are all items
MessageItemThe same object type as everything else now

Five object types with distinct lifecycles became two ideas. You send input items, you get output items back. Tool calls, reasoning traces, file search results all arrive as typed entries in one list. That is genuinely simpler, and it is also why find-and-replace will not do it.

The polling loop goes away

If you wrote against Assistants you have some version of this in your codebase:

run = client.beta.threads.runs.create(thread_id=t.id, assistant_id=a.id)
while run.status in ("queued", "in_progress"):
    time.sleep(0.5)
    run = client.beta.threads.runs.retrieve(thread_id=t.id, run_id=run.id)

Plus the requires_action branch, where you run your functions, call submit_tool_outputs, and go back to polling. Everyone built a state machine around that. Everyone’s state machine had a subtle bug in it.

In Responses the call returns when it returns:

resp = client.responses.create(
    model="gpt-5.1",
    conversation=conv.id,
    input=[{"role": "user", "content": "What changed in Q3?"}],
    tools=[{"type": "file_search", "vector_store_ids": [vs.id]}],
)

Function calling still costs a round trip. The model emits a function_call item, you execute it, you send a function_call_output item back. But that is a request and a response, not a poll against an object with seven statuses. Deleting the state machine is the best part of this migration.

Retention is the part your compliance team will notice

State has three shapes now, and they are not equivalent.

Three ways to hold conversation history in the Responses API and how long each one lasts. Attaching a conversation id stores items with no expiry. Chaining with previous_response_id stores response objects for 30 days by default. Setting store to false keeps the history in your own database and leaves nothing at rest on OpenAI. Three ways to keep the history, three retention answers conversation=<id> Items stored by OpenAI No TTL. They stay until you delete them. previous_response_id Response objects stored by OpenAI 30 days by default, then the chain is gone. store: false History in your own database Nothing at rest on OpenAI, more tokens on the wire.
From the OpenAI conversation state guide, checked 22 August 2026.

The documented behavior is that response objects are saved for 30 days by default, and that conversation objects and the items in them are not subject to that TTL. So the migration that feels most natural, swapping threads for conversations one to one, quietly moves your retention posture from “OpenAI holds this for 30 days” to “OpenAI holds this until we say otherwise.” Threads behaved the same way, so you are not worse off than you were. You are worse off than whatever anyone wrote down after reading the Responses default.

The third row is the interesting one. Assistants never offered it. You hold the history yourself, pass it as input every time, and nothing persists on OpenAI’s side. For regulated workloads that trade is usually right, and you pay for it in tokens.

Which brings up the billing detail people get wrong: chaining with previous_response_id does not make prior turns free. All previous input tokens in the chain are billed as input tokens. Prompt caching may hit the repeated prefix, automatically, without a guarantee. Model the spend against your real conversation lengths before you promise anyone a number.

Tools carry over, the meters look different

file_search and vector stores come across. So does code_interpreter, and web_search, plus things Assistants never had: computer use, image generation, remote MCP servers. You attach the tool to the response instead of to the assistant.

Check the meters against your current bill, because the shapes differ. File search is $0.10 per GB of vector storage per day with the first GB free, plus $2.50 per 1,000 tool calls. Code interpreter is priced per 20-minute session and scales with container memory: $0.03 at 1 GB, $0.12 at 4 GB, up to $1.92 at 64 GB. Both figures came off the OpenAI pricing page on 22 August 2026.

Storage billed daily means abandoned vector stores cost money for as long as they exist. If your Assistants setup created a store per user session and never cleaned up, this week is a good excuse to find out what you are paying to keep alive.

The gaps the guide does not lead with

Feature parity is the official line and it is mostly true. Two exceptions are worth knowing before you scope the work.

Prompt objects are dashboard-first. You could create assistants programmatically, which meant per-tenant provisioning at runtime, versioning in code, management through Terraform. Prompt creation currently runs through the dashboard. If your product spins up assistants per customer, the replacement is to inline instructions and tools into each response call instead. That works. It also moves configuration out of a managed object and into your application, which is a different architecture than the one you have.

Truncation control is thinner. Assistants managed conversation length for you. Now you decide what history to send and when to summarize, which is real work on long-running conversations and real token cost when you get it wrong.

Azure: same date, different destination, and a tool that helps

The common assumption is that Microsoft runs its own calendar and you get extra runway. You do not. Microsoft’s answer is that the Assistants API “is deprecated and will be retired on August 26, 2026,” the same day, with Microsoft Foundry Agents as the destination.

Unlike OpenAI, Microsoft ships a migration tool. It rewrites code constructs: agent definitions, thread creation, message creation, run creation. It does not move state, and the docs say so plainly: past runs, threads, and messages stay behind. Which means the Azure story has the same four-day export problem as the OpenAI one, wrapped in better tooling for the part that was never urgent.

One naming trap worth clearing up in planning meetings, because it burns an hour every time: Foundry agents (classic) retire on 31 March 2027. That is a different product on a different clock. If someone tells you they have until 2027, check which one they are actually running.

If you use a third-party host that emulated the Assistants wire format, get their timeline in writing. Some will keep the surface alive past August as a differentiator. That is a fine bridge and a bad destination.

Is Responses even where you want to land?

Fair question, and this is the second forced migration OpenAI has handed developers in short order. If you are rewriting anyway, it is worth an afternoon asking whether you want to be this exposed to one vendor’s roadmap.

Routing through a gateway is one answer. LiteLLM, Portkey, or OpenRouter give you one interface across providers, so the next deprecation is a config change rather than a sprint, and you pay in an extra hop plus some lag on brand-new capabilities. Moving to a framework is the other. LangGraph, Pydantic AI, and Mastra abstract state and tool orchestration so provider changes hit one adapter. Heavier upfront, and you have traded vendor lock-in for framework lock-in, which is a trade rather than an escape.

My read: if your Assistants usage is chat plus file search, go straight to Responses and finish in a week. If you built real orchestration on top of runs and tool outputs, you are rewriting that layer regardless, so spend two extra days deciding whether it should sit behind an abstraction this time.

What four days buys

Not the migration. The migration takes as long as it takes and the endpoints failing is a bad afternoon, not a lost quarter.

What it buys is the data. Grep for beta.threads, beta.assistants, and submit_tool_outputs. Count your threads. Write the dumbest possible exporter that writes raw JSON to disk, run it tonight, and check that the files have content in them before you go to bed. The clever version can be written any time. The source data cannot.

Keep reading