Migrating from the OpenAI API to the Claude API

The request shape is close enough to migrate in an afternoon — and different enough that a blind cutover breaks in three predictable places: the system message, the required token limit, and tool calling.

In short: Three predictable breakages: the system prompt moves to a top-level field, max_tokens becomes required, and tool calling changes schema. Everything else — messages, streaming, temperature — carries over. Run both APIs in shadow mode before cutting traffic.
You need a key before the code below runs. Create an account, generate a key, and copy the base URL (https://aicomp.ai/v1). Create one free →
Check current rates → Free to sign up · $1 minimum top-up · No prepayment

Side-by-side request

OpenAI style:

resp = client.chat.completions.create(
    model="gpt-5.6-luna",
    messages=[
        {"role": "system", "content": "You are a careful editor."},
        {"role": "user", "content": "Proofread this."},
    ],
)

Claude style:

resp = client.messages.create(
    model="claude-sonnet-5",
    system="You are a careful editor.",   # top-level, not a message
    max_tokens=1024,                      # required
    messages=[{"role": "user", "content": "Proofread this."}],
)
print(resp.content[0].text)

The mapping table

ConceptOpenAIClaude
System prompt{"role":"system"} inside messagesTop-level system parameter
Token limitmax_tokens, optionalmax_tokens, required
Text outputchoices[0].message.contentcontent[0].text
Input usageprompt_tokensinput_tokens
Output usagecompletion_tokensoutput_tokens
Stop sequencesstopstop_sequences
Tool callingfunctions / toolstools with input_schema
JSON moderesponse_formatPrefill or tool-based extraction
Auth headerAuthorization: Bearerx-api-key + anthropic-version

Tool calling: the part that needs real rewriting

OpenAI puts arguments in function.arguments as a JSON string. Claude returns a tool_use content block with an already-parsed input object, and you return results as a tool_result block rather than a message with a role. Budget most of your migration time here.

tools = [{
    "name": "get_weather",
    "description": "Current weather for a city",
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]

What it costs after you move

Comparing the OpenAI and Claude models teams usually evaluate against each other
ModelGateway rate
in / out per 1M tokens
Official list
in / out per 1M tokens
Diff
gpt-5.6-luna$0.1 / $0.6$0.2 / $1.250%
claude-sonnet-5$1 / $5$2 / $1050%
claude-opus-5$2.5 / $12.5$5 / $2550%

Rates checked 2026-09-16. Gateway rates move with upstream promotions — verify the current number in your dashboard before committing to a budget.

Do not assume the migration saves money. Compare tokens per task on real traffic, not list price per million — models differ in how verbose they are, and output is the expensive side.

A cutover plan that does not hurt

  1. Shadow mode. Send the same prompts to both, store both outputs, ship the OpenAI one.
  2. Diff quality. Review where they disagree; those are your risk areas.
  3. Route 10%. Real traffic, real latency, real cost.
  4. Compare cost per task. Not per token — per completed task.
  5. Ramp. Increase only after a week of clean metrics.

Common errors during migration

ErrorCause
max_tokens is requiredClaude requires it; OpenAI made it optional
Invalid system message positionSystem prompt left inside messages
401 / authentication_errorUsing Authorization: Bearer instead of x-api-key
Tools ignoredSchema passed as parameters rather than input_schema

FAQ

Can I reuse my OpenAI code with Claude?

Partly. If you went through an OpenAI-compatible layer, changing the base URL and key may be enough. If you called the OpenAI API directly, you need the changes in the mapping table above — mostly the system field, max_tokens, and tool calling.

What is the biggest gotcha in the migration?

max_tokens is required on Claude and optional on OpenAI. Requests that worked for months will fail with a validation error until you set it.

Does the response format change?

Yes. Instead of choices[0].message.content you read content[0].text, and usage is reported as input_tokens and output_tokens rather than prompt_tokens and completion_tokens.

How do I keep costs under control during migration?

Run both in parallel on a sample first, measure tokens per task on each, then cut over. Migrating blind is how teams double their bill on day one.

Can I migrate gradually?

Yes, and you should. Route a percentage of traffic, compare quality and cost for a week, then increase. The per-task cost difference is usually visible within a day of real traffic.

Related

Get API access