← Blog

Claude API Migration Guide: Moving From GPT

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

Migrating from the GPT API to Claude isn't a drop-in swap, but it's also not a rewrite. The core differences are predictable once you know where to look: how system prompts work, how messages are structured, how streaming events are shaped, and how tool calling is defined. This guide walks through each of those differences with working code so you can move an existing GPT integration to Claude without guessing.

The short version: both APIs use a messages-based chat format over HTTPS with JSON bodies, but the field names, response envelopes, and streaming event types diverge enough that a line-by-line port will break. Budget a few hours for a typical single-endpoint integration, more if you're using function calling or vision inputs heavily.

Authentication and endpoint differences

GPT API calls typically go to https://api.openai.com/v1/chat/completions with an Authorization: Bearer sk-... header. Claude's native API uses https://api.anthropic.com/v1/messages with an x-api-key header and an anthropic-version header.

If you're routing through SubToAPI instead of calling Anthropic directly, the shape stays the same but the header goes back to a standard bearer token, which is closer to what GPT developers are used to:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet-20241022",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Explain database indexing"}]
  }'

This matters if your team already has API key management, billing, and usage dashboards built around a single bearer-token pattern — you don't need a second auth model just for Claude. See /docs/quickstart for the full setup.

Request body: what changes

The biggest structural difference is the system prompt. In the GPT API, the system message lives inside the messages array with role: "system". In Claude's API, it's a top-level system parameter, separate from messages:

{
  "model": "claude-3-5-sonnet-20241022",
  "system": "You are a concise technical writing assistant.",
  "max_tokens": 1024,
  "messages": [
    {"role": "user", "content": "Summarize this changelog"}
  ]
}

Other notable differences:

If you're doing this port manually across a large codebase, expect to touch every call site that builds a message array. See /docs/messages for the full request/response reference.

Response shape

GPT wraps the reply in choices[0].message.content. Claude returns a content array directly on the response object, where each element is a content block:

{
  "id": "msg_01...",
  "type": "message",
  "role": "assistant",
  "content": [
    {"type": "text", "text": "Here's the summary..."}
  ],
  "stop_reason": "end_turn",
  "usage": {"input_tokens": 24, "output_tokens": 118}
}

If your existing code does response.choices[0].message.content, the Claude equivalent is response.content[0].text. Also note stop_reason values differ from GPT's finish_reason — Claude uses end_turn, max_tokens, stop_sequence, and tool_use instead of stop, length, and function_call.

Streaming

Both APIs support server-sent events, but the event types are different. GPT streams data: chunks with incremental delta.content strings. Claude streams named event types — message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop — each carrying its own payload shape:

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "content-type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-3-5-sonnet-20241022",
    max_tokens: 1024,
    stream: true,
    messages: [{ role: "user", content: "Write a haiku about caching" }]
  })
});

const reader = res.body.getReader();
// parse SSE lines, watch for event: content_block_delta

If you built a custom SSE parser for GPT's flat delta format, plan to rewrite it around Claude's typed events. Full details and a complete parsing example are in /docs/streaming.

Tool use vs function calling

GPT's functions/tools parameter and Claude's tools parameter are conceptually the same — both let the model request a structured call instead of free text — but the schema and response flow differ. Claude returns a tool_use content block with an id, name, and input object, and expects you to send the result back as a tool_result content block in a follow-up user message rather than a dedicated function-role message.

{
  "type": "tool_use",
  "id": "toolu_01...",
  "name": "get_weather",
  "input": {"city": "Lisbon"}
}

Anywhere your GPT code checks finish_reason === "function_call", the Claude equivalent is checking stop_reason === "tool_use" and inspecting the content blocks. This is one of the more error-prone parts of a migration — see /docs/tools if you want the request/response pairs spelled out.

Why proxy instead of migrating raw

If the goal is just "get Claude working behind our existing OpenAI-shaped billing and key infrastructure," a full raw migration to Anthropic's SDK isn't always necessary. SubToAPI sits in front of Claude and gives you application API keys (sub_live_...), per-team usage metadata, and streaming/tool support without needing separate Anthropic billing per client. You still write to the Messages format described above, but auth, rate limits, and seat management are handled in one dashboard. Plans start at €9/month for solo use, with team and scale tiers at /pricing, and there's a free trial at /signup.

questions

Do I need to rewrite my entire prompt library when migrating from GPT to Claude? Not necessarily. Move system instructions out of the messages array into the top-level system field, and re-test few-shot examples — Claude tends to respond well to XML-tagged structure in prompts even though it's not required.

Can I run GPT and Claude side by side during migration? Yes. Most teams keep both integrations behind a thin adapter layer that normalizes the response shape, run a subset of traffic through Claude, and compare output quality and latency before cutting over fully.

What's the fastest way to test the migration without touching billing infrastructure? Route calls through a service like SubToAPI during the trial period at /signup so you can validate request/response handling and streaming before deciding whether to manage direct Anthropic billing yourself.

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 →