← Blog

How to Switch from OpenAI to Claude API

2026-09-24 · 5 min read · SubToAPI Team

Switching from OpenAI to Claude API mainly means changing three things: the request shape, how you handle the system prompt, and how you parse the response. The underlying pattern — send messages, get a completion, optionally stream tokens or call tools — stays the same, so a working OpenAI integration usually maps to Claude in a few hours, not a rewrite.

This guide walks through the concrete differences you'll hit during migration, with code for both sides so you can compare directly.

Why teams switch

Most migrations happen for one of three reasons: Claude's longer context windows, its tool-use behavior on complex multi-step tasks, or wanting a second provider for redundancy and pricing leverage. Whatever your reason, the migration itself is mechanical once you know where the APIs diverge.

Step 1: Map the request format

OpenAI's Chat Completions API takes a flat messages array where the system prompt is just another message with role: "system":

// OpenAI
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [
    { role: "system", content: "You are a helpful support agent." },
    { role: "user", content: "How do I reset my password?" }
  ],
  max_tokens: 500
});

Claude's Messages API pulls the system prompt out into its own top-level field:

// Claude
const response = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  system: "You are a helpful support agent.",
  messages: [
    { role: "user", content: "How do I reset my password?" }
  ],
  max_tokens: 500
});

max_tokens is required in Claude requests — there's no server-side default like OpenAI's implicit cap. If you omit it, the call fails, so audit any code that relied on OpenAI's defaults.

Step 2: Update response parsing

OpenAI returns the assistant text at choices[0].message.content. Claude returns a content array of blocks, since a single response can mix text and tool calls:

// OpenAI
const text = response.choices[0].message.content;

// Claude
const text = response.content
  .filter(block => block.type === "text")
  .map(block => block.text)
  .join("");

If you're only ever doing plain text completions, this is a one-line change. If you're doing tool use, you'll need to iterate the content blocks and check block.type === "tool_use" for structured calls — see /docs/tools for the exact shape.

Step 3: Rework streaming

OpenAI streams delta chunks with data: lines that carry partial choices[0].delta.content. Claude uses a named event stream (message_start, content_block_delta, message_stop, etc.), which is more verbose but easier to reason about because each event type is unambiguous:

const stream = await anthropic.messages.stream({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a haiku about APIs" }]
});

for await (const event of stream) {
  if (event.type === "content_block_delta") {
    process.stdout.write(event.delta.text);
  }
}

If your frontend just reads a token stream and appends it to a buffer, the migration is invisible to the UI layer — you're only changing which event type you extract text from. Full details are in /docs/streaming.

Step 4: Handle function/tool calling differences

OpenAI's tools use a function wrapper with parameters as JSON Schema. Claude's tools are flatter — name, description, and input_schema:

// Claude tool definition
const tools = [{
  name: "get_weather",
  description: "Get current weather for a location",
  input_schema: {
    type: "object",
    properties: {
      location: { type: "string" }
    },
    required: ["location"]
  }
}];

The control flow is the same: the model returns a tool call, you execute it, and send the result back as a message with role: "user" and a tool_result content block. The naming differs but the loop is identical, so most tool-calling code ports over by renaming fields rather than restructuring logic.

Step 5: Check model-specific quirks

A few things trip people up during migration:

Step 6: Decide how you're authenticating and billing

If you're calling the Anthropic API directly, you'll need an Anthropic account, billing setup, and to manage API keys per environment yourself. If you want to skip that and reuse a Claude subscription you already pay for, SubToAPI turns it into a standard HTTPS API: you get sub_live_... keys, the same Messages-style request/response shape, streaming, tool use, and usage metadata per key — without setting up separate Anthropic billing. This is particularly useful mid-migration, when you want to test Claude in production against real traffic before committing to a long-term billing setup. See /docs/quickstart to get a key running in a few minutes, and /docs/messages for the exact request format.

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 500,
    "messages": [{"role": "user", "content": "How do I reset my password?"}]
  }'

Step 7: Run both providers side by side

Before cutting over fully, route a percentage of traffic to Claude and compare output quality, latency, and cost on real requests. Keep your OpenAI integration behind a feature flag for a few weeks — this is the safest way to catch prompt regressions that only show up with real user inputs, not just your test cases.

Testing checklist

Full request/response documentation is at /docs, and pricing for a hosted, subscription-based setup is at /pricing if you'd rather skip managing a separate Anthropic bill during and after migration.

FAQ

Do I need to rewrite my entire application to switch from OpenAI to Claude? No. If your architecture separates the LLM call from the rest of your logic, you're mainly rewriting one client function — the request builder, response parser, and streaming handler. Business logic, prompts, and UI usually don't need major changes.

Can I run OpenAI and Claude side by side during migration? Yes, and it's the recommended approach. Add a provider flag to your request layer, route a subset of traffic to Claude, and compare output quality and latency before fully cutting over.

Is Claude's tool-calling compatible with OpenAI's function-calling format? Not directly — field names differ (input_schema vs parameters) and the response structure uses content blocks instead of a tool_calls array. The control flow (call, execute, return result) is the same, so migration is a rename-and-restructure task rather than a redesign.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →