πŸ”„ Assistants API Compatibility Shim avatar

πŸ”„ Assistants API Compatibility Shim

Pricing

Pay per event + usage

Go to Apify Store
πŸ”„ Assistants API Compatibility Shim

πŸ”„ Assistants API Compatibility Shim

Drop-in proxy keeping OpenAI Assistants API calls working past the August 26, 2026 sunset, plus a Thread→Conversation migration utility.

Pricing

Pay per event + usage

Rating

0.0

(0)

Developer

Stephan Corbeil

Stephan Corbeil

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

11 hours ago

Last modified

Share

102 days until OpenAI Assistants API shutdown. (Hard sunset: August 26, 2026 β€” countdown computed against 2026-05-16.)

Your code uses POST /v1/threads, POST /v1/threads/{id}/runs, and the rest of the OpenAI Assistants API. OpenAI is sunsetting that surface on August 26, 2026 in favor of the Responses API, which has a completely different shape (no threads, different message format, different tool wiring, different run lifecycle).

This actor is a drop-in HTTP proxy that keeps your Assistants-shaped calls working β€” and a one-shot migration utility for moving thread history into Responses conversations cleanly.


⏳ The sunset countdown

Hard cutoff date2026-08-26
As of 2026-05-16102 days remaining
Affected surface/v1/assistants, /v1/threads, /v1/threads/{id}/messages, /v1/threads/{id}/runs, /v1/threads/{id}/runs/{id}
Replacement surface/v1/responses, /v1/conversations
What breaks if you do nothingEvery hardcoded SDK call against openai.beta.threads.* or openai.beta.assistants.* returns 410 Gone after the cutoff

If you own a SaaS, an internal automation, a Zapier/Make/n8n step, or a customer-facing chatbot that was built against the Assistants API in 2024–2025, you have one of two options:

  1. Rewrite every integration before the deadline. Industry estimate: 5–50 dev-days per integration at $150–300/hr (β‰ˆ $6k–$120k per codebase).
  2. Point your client at this shim's URL instead of api.openai.com. Zero code change. Buys you runway. Comes with a migration utility for when you're ready to cut over cleanly.

🧭 Two ways to use it

1. Live proxy β€” zero code change

Swap your client's base URL. That's it.

- base_url = "https://api.openai.com/v1"
+ base_url = "https://<actor-id>.apify.actor/v1"

Your existing client.beta.threads.create(...), client.beta.threads.messages.create(...), client.beta.threads.runs.create(...) calls keep working. Internally, the shim:

  1. Receives the Assistants-shaped request
  2. Translates it to the Responses API shape
  3. Calls OpenAI on your behalf using your own API key (passed via the standard Authorization: Bearer sk-... header β€” never stored)
  4. Translates the response back to the Assistants shape your code expects
  5. Returns it

2. Migration utility β€” one-shot bulk cutover

When you're ready to cut over to native Responses calls, use the migration utility to port thread history.

curl -X POST "https://<actor-id>.apify.actor/v1/migrate?source_thread_id=thread_abc123" \
-H "Authorization: Bearer sk-..."
curl -X POST "https://<actor-id>.apify.actor/v1/bulk_migrate" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"thread_ids":["thread_abc","thread_def","thread_ghi"]}'

You get back a conversation_id per thread, with the message history seeded into the new Responses conversation. Replay or branch from there with native Responses calls. Old threads are not deleted.


πŸ— Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Assistants-shape β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Responses-shape β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Your client β”‚ ───────────────────▢ β”‚ Apify Standby actor β”‚ ───────────────────▢ β”‚ api.openai β”‚
β”‚ (unchanged) β”‚ Authorization: Bearerβ”‚ (this shim) β”‚ Same Bearer token β”‚ .com/v1 β”‚
β”‚ SDK / cURL β”‚ β”‚ β€’ translate request β”‚ β”‚ /responses β”‚
β”‚ β”‚ ◀─────────────────── β”‚ β€’ call upstream β”‚ ◀─────────────────── β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Assistants-shape β”‚ β€’ translate response β”‚ Responses-shape β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β€’ thread state in KV β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό (optional)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Apify Key-Value Store β”‚ thread_id β†’ conversation_id
β”‚ (survives restarts) β”‚ message history mirror
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The actor runs in Apify Standby mode β€” it's an always-on HTTP server, not a one-shot scrape. You get a stable hostname (https://<actor-id>.apify.actor), the container stays warm under load, and idles down after 180 s of inactivity.


πŸ”€ Translation table

The translation logic is the heart of the shim. Every Assistants concept maps to a Responses concept:

Assistants API conceptResponses API conceptNotes
thread_idconversation_idCreated lazily on first run; the shim stores the mapping.
message.content (array of {type:"text", text:{value}})input (string, or array of {role, content})Multi-part text gets joined when possible; image parts pass through as input_image.
assistant_id + assistant.toolsRequest-time tools arrayThe shim keeps assistant configs in memory + KV; injects them on every run.
assistant.instructionsinstructions field on the Responses requestDirect passthrough.
run.status lifecycle (queued β†’ in_progress β†’ completed)Single synchronous Responses callThe shim returns a completed run immediately β€” Responses is not long-polling.
run_stepsReassembled from response.output[] itemsTool calls and message outputs are flattened into the run-step shape your client expects.
Assistants tools: [{type:"function", function:{...}}]Responses tools: [{type:"function", name, description, parameters}]Schema repacks; semantics identical.
Assistants tools: [{type:"code_interpreter"}]Responses tools: [{type:"code_interpreter", container:{type:"auto"}}]Translates, but containers are auto-provisioned β€” explicit container IDs aren't carried over.
Assistants tools: [{type:"file_search", vector_store_ids:[...]}]Not supported in v1Vector stores don't migrate 1:1; see "Unsupported" below.
Assistants messages.attachments (file_ids)input_image / input_file partsFile-id passthrough for images; arbitrary file attachments β†’ v2.
Assistants metadatametadataStored and round-tripped.

βœ… Supported endpoints (v1)

  • POST /v1/threads β€” create thread
  • GET /v1/threads/{thread_id}/messages β€” list messages
  • POST /v1/threads/{thread_id}/messages β€” append message
  • POST /v1/threads/{thread_id}/runs β€” synchronous run (returns completed)
  • GET /v1/threads/{thread_id}/runs/{run_id} β€” get run
  • POST /v1/assistants β€” create assistant (stored in actor state)
  • GET /v1/models β€” passthrough to OpenAI
  • POST /v1/migrate?source_thread_id=... β€” migrate one thread
  • POST /v1/bulk_migrate β€” migrate up to thousands of threads in one call

🚧 Not yet supported (v2 roadmap)

  • /v1/vector_stores/* and file_search tool
  • code_interpreter with explicit container IDs (auto-containers do work)
  • Streaming (stream:true) on runs β€” synchronous-only in v1
  • assistants.list / threads.delete (low priority β€” most clients don't need them)

If you hit an unsupported endpoint, the shim returns HTTP 501 with unsupported_endpoint so your error handling can detect it cleanly.


🎯 Use cases

  1. SaaS app with hardcoded Assistants integration. You shipped a customer-facing AI assistant feature in 2024 against openai.beta.threads. Rewriting before August 26 is a full sprint. Swap base URL β†’ done.
  2. Zapier / Make / n8n workflow. Your no-code automation has an "OpenAI Assistant" step. Point the custom-base-URL field at the shim and the workflow keeps running.
  3. Customer chatbot with persistent thread history. Per-user thread_ids are stored in your DB. You can't lose conversation continuity. The shim preserves thread_id semantics; migration utility ports history to conversation_id when you're ready.
  4. Internal AI tools (Slack bot, ops dashboard). Built once, forgotten about, still in production. Shim keeps them alive past sunset while you reprioritize a rewrite.
  5. Multi-tenant AI platform. You expose "bring your own assistant" to customers. Each tenant's hardcoded Assistants integrations need a stopgap β€” point them all at the shim.
  6. Agentic frameworks pinned to old OpenAI SDK versions. LangChain/LlamaIndex chains that wrap AssistantAgent keep working without a framework upgrade.

βš™οΈ Input schema

FieldTypeDefaultDescription
openaiApiKeystring (secret)""Fallback OpenAI key used when an incoming request has no Authorization header. Most live-proxy users leave this empty β€” clients send the key per request. Set it for migration mode if your migration script doesn't send auth.
modeenum"both""live-proxy", "migration-only", or "both".
persistThreadStatebooleantrueMirror thread/assistant state to the actor's Key-Value Store so it survives restarts.

πŸš€ Quick start

Step 1 β€” Start the actor in Standby mode

Run the actor once with the default input. Apify keeps the container warm. You'll get a Standby URL like https://nexgendata--assistants-api-compatibility-shim.apify.actor.

Step 2 β€” Repoint your client

Python (OpenAI SDK)

from openai import OpenAI
client = OpenAI(
api_key="sk-...",
base_url="https://<actor-id>.apify.actor/v1",
)
# This call used to go to api.openai.com β€” now it goes through the shim.
thread = client.beta.threads.create()
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="What's the capital of France?",
)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id="asst_xxx",
)
print(run.status) # "completed"

cURL β€” live proxy

# Create a thread
curl -X POST "https://<actor-id>.apify.actor/v1/threads" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{}'
# Append a user message
curl -X POST "https://<actor-id>.apify.actor/v1/threads/thread_xxx/messages" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"role":"user","content":"Hello"}'
# Run an assistant
curl -X POST "https://<actor-id>.apify.actor/v1/threads/thread_xxx/runs" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"assistant_id":"asst_xxx"}'

cURL β€” migration

# One thread
curl -X POST "https://<actor-id>.apify.actor/v1/migrate?source_thread_id=thread_xxx" \
-H "Authorization: Bearer sk-..."
# Bulk
curl -X POST "https://<actor-id>.apify.actor/v1/bulk_migrate" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"thread_ids":["thread_a","thread_b","thread_c"]}'

Python β€” bulk migration script

import httpx
ids = [...] # load from your DB
r = httpx.post(
"https://<actor-id>.apify.actor/v1/bulk_migrate",
headers={"Authorization": "Bearer sk-..."},
json={"thread_ids": ids},
timeout=600,
)
print(r.json()["success"], "migrated;", r.json()["failed"], "failed")

❓ FAQ

Q: How long until OpenAI shuts down the Assistants API? A: 102 days as of 2026-05-16. Hard cutoff is 2026-08-26. After that, every /v1/threads/* call returns 410 Gone.

Q: What happens to my thread history if I just keep using the live proxy? A: The shim stores thread state (messages, assistant_id mapping, conversation_id mapping) in the Apify Key-Value Store. It survives restarts. But the upstream OpenAI threads themselves go away on 2026-08-26 β€” meaning the shim becomes the new source of truth for history. Run the migration utility before the cutoff to seed the data into Responses conversations.

Q: Can I migrate without using the live proxy? A: Yes β€” set mode: "migration-only" and the live-proxy routes are disabled. You just call /v1/bulk_migrate once and get the mapping table.

Q: What's the latency overhead of the shim? A: ~15–40 ms p50 added on top of the OpenAI call (one extra hop, JSON repack). Standby keeps the container warm so there's no cold start after the first request.

Q: Are file_search / vector stores supported? A: Not in v1 β€” they're punted to v2 because vector_store_ids don't migrate 1:1 to Responses' file/container model. Hitting /v1/vector_stores/* returns HTTP 501 with unsupported_endpoint. If you depend on file_search, migrate those specific call sites manually.

Q: How does pricing compare to migration consulting? A: A typical agency engagement to rewrite Assistants integrations runs $6k–$120k per codebase. The shim is $0.05 per proxied request, $0.20 per thread migrated, and $12 per 100-thread bulk batch. Even a heavy user running 100k requests and migrating 10k threads pays ~$5,000 β€” and gets a runway, not a one-time rewrite.

Q: What if OpenAI extends the deadline? A: Then you keep running the shim cheaply. There's no commitment β€” pay-per-event pricing means you pay $0 when your traffic is $0.

Q: Do I need my own OpenAI API key? A: Yes β€” BYO. The shim is a translation layer; it does not resell OpenAI access. Pass your key in the Authorization: Bearer sk-... header on every request (the standard SDK behavior). Keys are never logged or persisted.

Q: Is the container multi-tenant safe? Can two of my customers share one shim? A: Thread state is keyed by thread_id, which is globally unique. Two tenants with separate thread_ids won't collide. But for strict tenant isolation we recommend one shim run per tenant β€” Apify makes that cheap.


πŸ†š Comparison

DIY rewriteOpenAI's official migration guideCleric / Bedrock proxiesThis shim
Code changes requiredAll call sitesAll call sitesSome configOne base URL line
Engineering effort5–50 dev-days5–50 dev-days1–5 dev-days5 minutes
Migrates existing threadsManualManual scripts you writeNoYes (bulk utility)
Pricing$6k–$120k consultingFree (your time)$500+/mo flatPPE: $0 idle, ~$0.05/request
HostedYou hostN/AThey hostApify hosts
Source availableβ€”β€”ClosedSourced from this actor

🀝 Sister actors from NexGenData

If this shim solved your migration problem, check out our other Apify-hosted AI infra:


πŸ’Έ Don't have an Apify account?

Sign up free β€” $5 free credits to host this shim and keep your Assistants integrations alive past the August 2026 sunset.


πŸ“œ License

Source distributed with the actor. BYO OpenAI key β€” we never store, log, or proxy-decrypt your credentials.