OpenAI Agents SDK on a Custom OpenAI-Compatible API: Chat Completions, Responses and the Tracing Trap Aman Kumar published a guide showing that the OpenAI Agents SDK (openai-agents 0.23.1) fails with 404 NotFoundError responses when pointed at a custom OpenAI-compatible gateway unless developers call set_default_openai_api("chat_completions") and set_tracing_disabled(True), because the SDK defaults to the Responses API and exports traces to OpenAI. Kumar verified three working setups — a per-agent OpenAIChatCompletionsModel with an AsyncOpenAI client, process-wide defaults via set_default_openai_client, and an OpenAIProvider with use_responses=False passed through RunConfig — against a local mock implementing only /v1/chat/completions. The examples use APIClaw, a gateway Kumar says he builds, with base URL https://apiclaw.biz/v1 and model IDs such as deepseek-v4-flash, deepseek-v4-pro and claude-sonnet-5-5. OpenAI Agents SDK on a Custom OpenAI-Compatible API: Chat Completions, Responses and the Tracing Trap I'm Aman Kumar. The OpenAI Agents SDK openai-agents on PyPI is a clean way to run tool-calling agents in Python. Out of the box it talks to api.openai.com over the Responses API and ships traces back to OpenAI. Point it at another OpenAI-compatible server without changing those defaults and you ge I'm Aman Kumar. The OpenAI Agents SDK openai-agents on PyPI is a clean way to run tool-calling agents in Python. Out of the box it talks to api.openai.com over the Responses API and ships traces back to OpenAI. Point it at another OpenAI-compatible server without changing those defaults and you get 404s that look like "the model is wrong" or mysterious network errors from the tracer. I wrote this against openai-agents 0.23.1 and verified the three working setups below on a local mock that only implements /v1/chat/completions. The examples use APIClaw, a gateway I build, so weigh that accordingly. Base URL is https://apiclaw.biz/v1. Copy the model ID from /v1/models or the model hub; I use YOUR MODEL ID below. On that hub you will see IDs like deepseek-v4-flash, deepseek-v4-pro or claude-sonnet-5-5. Crypto is on the site; other payment methods go through support. Swap host, key and model for whatever you run — vLLM, LiteLLM, Ollama's /v1 endpoint, or any server that speaks Chat Completions. Two defaults bite people: API shape. set default openai api defaults to "responses". Most third-party gateways implement /v1/chat/completions, not /v1/responses. Hitting Responses on a chat-only gateway returns NotFoundError 404 . Tracing. Unless you call set tracing disabled True , the SDK tries to export traces using an OpenAI API key. On a custom gateway that key is either absent or not valid for OpenAI's trace endpoint, so you get noise even when chat works. Fix both before you debug model IDs. from agents import set default openai api, set tracing disabled set tracing disabled True set default openai api "chat completions" Pass an AsyncOpenAI client with your base URL into OpenAIChatCompletionsModel. The agent never touches the Responses path. import asyncio from openai import AsyncOpenAI from agents import Agent, Runner, OpenAIChatCompletionsModel, set tracing disabled set tracing disabled True client = AsyncOpenAI base url="https://apiclaw.biz/v1", include /v1 api key="YOUR API KEY", model = OpenAIChatCompletionsModel model="YOUR MODEL ID", openai client=client, agent = Agent name="assistant", instructions="Reply in one short paragraph.", model=model, async def main : result = await Runner.run agent, "What is a base URL in an OpenAI client?" print result.final output asyncio.run main Runner.run sync ... works the same way if you do not want asyncio at the call site. When every agent in the process should use the same gateway, set the process defaults once. from openai import AsyncOpenAI from agents import Agent, Runner, set default openai client, set default openai api, set tracing disabled, set tracing disabled True set default openai client AsyncOpenAI base url="https://apiclaw.biz/v1", api key="YOUR API KEY" set default openai api "chat completions" agent = Agent name="assistant", instructions="Be concise.", model="YOUR MODEL ID", plain string is fine now result = Runner.run sync agent, "List three reasons to pin a base URL." print result.final output Order matters. If you leave the default API on "responses", the same client still posts to /v1/responses and a chat-only gateway 404s. OpenAIProvider is what RunConfig model provider=... expects when you want a provider object instead of globals. from openai import AsyncOpenAI from agents import Agent, Runner, OpenAIProvider, RunConfig, set tracing disabled set tracing disabled True client = AsyncOpenAI base url="https://apiclaw.biz/v1", api key="YOUR API KEY" provider = OpenAIProvider openai client=client, use responses=False agent = Agent name="assistant", instructions="Be concise.", model="YOUR MODEL ID", result = Runner.run sync agent, "Explain chat completions vs responses in one sentence.", run config=RunConfig model provider=provider , print result.final output use responses=False makes get model return OpenAIChatCompletionsModel. You can also pass base url= and api key= directly into OpenAIProvider instead of a prebuilt client. The OpenAI Python client appends /chat/completions to whatever you put in base url. base url Request path Typical result on an OpenAI-layout gateway https://apiclaw.biz/v1 /v1/chat/completions Works https://apiclaw.biz /chat/completions 404 https://apiclaw.biz/v1/chat/completions /v1/chat/completions/chat/completions 404 Always stop at /v1. Do not paste the full chat endpoint into base url. On my chat-only mock, leaving the default API on "responses" raised: openai.NotFoundError: Error code: 404 - {'error': {'message': 'Responses API not available on this gateway', 'type': 'invalid request error'}} That is not a bad model ID. The SDK is calling a different route. Switch to chat completions any of A/B/C above and retry with the same model string. Function tools go through the same model wrapper. Minimal example: from agents import Agent, Runner, OpenAIChatCompletionsModel, function tool, set tracing disabled from openai import AsyncOpenAI set tracing disabled True client = AsyncOpenAI base url="https://apiclaw.biz/v1", api key="YOUR API KEY" @function tool def add a: int, b: int - int: """Add two integers.""" return a + b agent = Agent name="math", instructions="Use the add tool for arithmetic.", model=OpenAIChatCompletionsModel model="YOUR MODEL ID", openai client=client , tools= add , print Runner.run sync agent, "What is 7 + 5?" .final output If a gateway strips tool calls from chat responses, the agent will loop or answer without tools — that is a server capability issue, not an Agents SDK base-URL bug. Check one raw /v1/chat/completions request with "tools" in the body before you dig into the runner. pip install openai-agents I tested 0.23.1 . set tracing disabled True unless you intentionally send traces to OpenAI. Force Chat Completions: OpenAIChatCompletionsModel, or set default openai api "chat completions" , or OpenAIProvider ..., use responses=False . base url ends with /v1, not /v1/chat/completions. Model ID is the full string from the gateway's model list, not a short nickname. Confirm a plain client.chat.completions.create ... works before wrapping it in Agent. Once those six are right, the Agents SDK stops fighting the gateway and you can focus on instructions, tools and handoffs. Key Takeaways - •I'm Aman Kumar - •This story was reported by Dev.to , covering developments in the dev space. - •AI advancements continue to reshape industries — read the full article on Dev.to for complete coverage. 📖 Continue reading the full article: Read Full Article on Dev.to → https://dev.to/amankumar apiclaw/openai-agents-sdk-on-a-custom-openai-compatible-api-chat-completions-responses-and-the-tracing-184d