# Migrate from the OpenAI API to Claude: what changes in code

> Move OpenAI API code to Claude: keep the OpenAI SDK and change three settings, or switch to the Anthropic SDK. Field map, 400 errors to avoid, cost.

Updated: 2026-11-03

To move from OpenAI to Claude, you can keep the OpenAI SDK and change three settings: the base URL `https://api.5abeer.ai/v1`, a 5abeer key for Claude models, and a Claude model id such as `claude-sonnet-5.5`. For Claude's own settings, such as prompt caching, switch to the Anthropic SDK at `https://api.5abeer.ai`. Both routes take the same key.

## What changes when you move from OpenAI to Claude?

5abeer is OpenAI-compatible: change the base URL and keep your code. Three settings change:

| Setting | Before, on OpenAI | After, Claude on 5abeer |
| --- | --- | --- |
| Base URL | `https://api.openai.com/v1` | `https://api.5abeer.ai/v1` |
| Key | An OpenAI API key | A 5abeer key for Claude models |
| Model | A GPT model id | A Claude model id, such as `claude-sonnet-5.5` |

Then pick a route: keep the OpenAI SDK, the smallest change, or move to the Anthropic SDK, which reaches every Claude setting. How an SDK builds each address is in [What is a base URL](https://5abeer.ai/en/guides/what-is-base-url).

## Route 1: keep the OpenAI SDK

Put your key in the `KHABEER_API_KEY` environment variable, then change the client and the model id:

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.5abeer.ai/v1",
    api_key=os.environ["KHABEER_API_KEY"],
)

response = client.chat.completions.create(
    model="claude-sonnet-5.5",
    messages=[
        {"role": "system", "content": "Answer in two sentences."},
        {"role": "user", "content": "What is a context window?"},
    ],
)

print(response.choices[0].message.content)
```

In the Node.js library the option is called `baseURL`:

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.5abeer.ai/v1",
  apiKey: process.env.KHABEER_API_KEY,
});

const response = await client.chat.completions.create({
  model: "claude-sonnet-5.5",
  messages: [{ role: "user", content: "What is a context window?" }],
});

console.log(response.choices[0].message.content);
```

Both libraries also read `OPENAI_BASE_URL` and `OPENAI_API_KEY` when you don't pass them in code, so a project can switch by setting two variables and changing the model id. Code that calls `client.responses.create` keeps working too: Claude models are served on [Responses](https://5abeer.ai/en/docs/api/responses.md) as well.

## Which Claude model should I pick?

The Claude models you can call now:

| Model | Id | Input (ours) | Output (ours) | Context |
| --- | --- | --- | --- | --- |
| Claude Sonnet 5.5 | `claude-sonnet-5.5` | $0.60 | $3 | 1M |
| Claude Opus 5.5 | `claude-opus-5.5` | $1.20 | $6 | 1M |
| Claude Fable 5.1 | `claude-fable-5.1` | $3 | $15 | — |
| Claude Fable 5 | `claude-fable-5` | $3 | $15 | 1M |
| Claude Opus 4.6 | `claude-opus-4.6` | $1.50 | $7.50 | 1M |
| Claude Opus 4.7 | `claude-opus-4.7` | $1.50 | $7.50 | 1M |

Start with [`claude-sonnet-5.5`](https://5abeer.ai/en/models/claude-sonnet-5.5), the default on 5abeer, and try [`claude-opus-5.5`](https://5abeer.ai/en/models/claude-opus-5.5) or [`claude-fable-5.1`](https://5abeer.ai/en/models/claude-fable-5.1) on your hardest prompts; [Which Claude model to use](https://5abeer.ai/en/guides/which-claude-model) compares them. Copy each id as our catalog writes it, with a dot in the version, even where Anthropic's docs use a hyphen. `client.models.list()` returns the models your key can use, as in [List models](https://5abeer.ai/en/docs/api/models.md#ex-list).

## Route 2: move to the Anthropic SDK

Claude's own settings, such as `cache_control`, thinking settings and `output_config`, live in Anthropic's format. 5abeer is compatible with the Anthropic Messages API, so the official SDK works with it. Install it with `pip install anthropic`:

```python
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.5abeer.ai",
    api_key=os.environ["KHABEER_API_KEY"],
)

message = client.messages.create(
    model="claude-sonnet-5.5",
    max_tokens=4096,
    system="Answer in two sentences.",
    messages=[{"role": "user", "content": "What is a context window?"}],
)

print("".join(block.text for block in message.content if block.type == "text"))
```

Four things differ from the OpenAI code:

- The base URL has no `/v1`. The Anthropic SDK adds the path itself.
- `max_tokens` is required, and the instructions go in the top-level `system` field.
- Thinking is on by default on Claude 5 models. Anthropic counts thinking tokens toward `max_tokens` and bills them as output, so leave room: these samples use 4096.
- The reply is a list of blocks, and a `thinking` block can come before the text. Read the `text` blocks, not the first block.

The SDK also reads `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY` from the environment, as in [Base URLs](https://5abeer.ai/en/docs/start/base-urls.md#anthropic).

## How do Chat Completions and Messages differ?

The same conversation sits in different fields. Moving to Messages means renaming these:

| In Chat Completions | In Messages |
| --- | --- |
| Base URL `https://api.5abeer.ai/v1` | Base URL `https://api.5abeer.ai`, with no `/v1` |
| The `Authorization: Bearer` header | The `x-api-key` and `anthropic-version: 2023-06-01` headers; Bearer works too |
| A message with the `system` role | The top-level `system` field |
| `max_tokens` is optional | `max_tokens` is required |
| `choices[0].message.content` | The `text` blocks in `content` |
| `stop` | `stop_sequences` |
| `parameters` in a function | `input_schema` in a tool |
| A message with the `tool` role | A `tool_result` block in a `user` message |
| `reasoning_effort` | `effort` in `output_config` |
| `response_format` | A tool with a schema |
| `prompt_tokens` and `completion_tokens` | `input_tokens` and `output_tokens` |
| Chunks with a `delta` | Named events such as `content_block_delta` |

Each field is in the [Messages reference](https://5abeer.ai/en/docs/api/messages.md#params), and the same tool in each format is in [Tool calling](https://5abeer.ai/en/docs/api/tools.md#formats).

## Which OpenAI options work with Claude on 5abeer?

From our [Chat Completions reference](https://5abeer.ai/en/docs/api/chat-completions.md#params). An ignored option causes no error; it just has no effect.

| Option | With Claude models on 5abeer |
| --- | --- |
| `messages` with the `system`, `user`, `assistant` and `tool` roles | Supported, images included |
| `max_completion_tokens` or `max_tokens` | Supported |
| `stream` and `stream_options` | Supported |
| `tools` and `tool_choice` | Supported; keep `tool_choice` at `auto` (see below) |
| `reasoning_effort` | Supported |
| `temperature` and `top_p` | Passed to the model (see below) |
| `response_format` | Ignored: use a tool with a schema |
| `stop` | Ignored: use `stop_sequences` in [Messages](https://5abeer.ai/en/docs/api/messages.md#params) |
| `parallel_tool_calls`, `n`, `seed`, the penalties, `logprobs`, `logit_bias` and `user` | Ignored |

Anthropic's docs, checked on 2026-11-03, add two limits. On Claude 4.7 and later models, a non-default `temperature` or `top_p` returns a `400` error. On `claude-opus-5.5`, `claude-sonnet-5.5` and `claude-fable-5.1`, so does a forced tool call through `tool_choice`. Leave the sampling settings out, keep `tool_choice` at `auto`, and name the tool you want in the prompt.

## How do I get JSON from Claude?

`response_format` has no effect with Claude models on our Chat Completions, so ask for JSON through a tool. With the Anthropic SDK, describe a tool whose `input_schema` holds the fields you want, and read them from the `tool_use` block:

```python
tools = [{
    "name": "save_city",
    "description": "Save facts about a city.",
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {"type": "string"},
            "country": {"type": "string"},
            "population": {"type": "integer"},
        },
        "required": ["city", "country", "population"],
    },
}]

message = client.messages.create(
    model="claude-sonnet-5.5",
    max_tokens=4096,
    tools=tools,
    messages=[{"role": "user", "content": "Call save_city with the facts about Tokyo."}],
)

calls = [block for block in message.content if block.type == "tool_use"]
if calls:
    print(calls[0].input["city"], calls[0].input["population"])
else:
    print("No tool call, stop_reason:", message.stop_reason)
```

With `tool_choice` at `auto`, the model decides whether to call the tool, so keep the check for a missing `tool_use` block. How to send a tool's result back is in [Tool calling](https://5abeer.ai/en/docs/api/tools.md#loop).

## Does prompt caching carry over?

Not on its own. With Claude models, you mark what to cache by adding `cache_control` to a content block, and that field exists only in Messages. If your prompts repeat long instructions or documents, that alone is a reason to take route 2; see [Prompt caching](https://5abeer.ai/en/docs/api/caching.md#claude).

## Does streaming work?

Yes, on both routes. With the OpenAI SDK, `stream=True` works as before. With the Anthropic SDK, the `messages.stream` helper yields the text as it is written:

```python
with client.messages.stream(
    model="claude-sonnet-5.5",
    max_tokens=4096,
    messages=[{"role": "user", "content": "Write a short poem about the sea."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

The events behind it are in [Streaming](https://5abeer.ai/en/docs/api/messages.md#streaming).

## Common errors

| What you see | Likely cause | Fix |
| --- | --- | --- |
| `403` with `MODEL_PLATFORM_NOT_ALLOWED` | The key is for GPT or Gemini models | Create a key for Claude models, as in [One key per provider](https://5abeer.ai/en/docs/start/keys.md#per-provider) |
| `401` with `INVALID_API_KEY` | The code still sends an OpenAI key | Put your 5abeer key in the variable your code reads |
| `404` Not Found | `/v1` added to the Anthropic SDK's base URL | Use `https://api.5abeer.ai` |
| `400` on a request that worked with GPT | A non-default `temperature` or `top_p`, or a forced tool call | Leave sampling settings out; set `tool_choice` to `auto` |
| `stop_reason` is `max_tokens`, with little or no text | `max_tokens` ran out, often on thinking | Raise `max_tokens` |
| `AttributeError` on `choices` | Chat Completions code reads a Messages reply | Read the `text` blocks in `content` |

Every code is in [Errors and fixes](https://5abeer.ai/en/docs/help/errors.md#codes) and [Limits](https://5abeer.ai/en/docs/api/limits.md#model-errors).

## Test it with one request

Before you change a large project, send one request with your Claude key. A reply means the base URL, the key and the model id are right:

```bash
curl --request POST "https://api.5abeer.ai/v1/messages" \
  --header "x-api-key: $KHABEER_API_KEY" \
  --header "Content-Type: application/json" \
  --header "anthropic-version: 2023-06-01" \
  --data '{"model":"claude-sonnet-5.5","max_tokens":512,"messages":[{"role":"user","content":"Write a product idea in one sentence."}]}'
```

```python
import os
import requests

response = requests.post(
    "https://api.5abeer.ai/v1/messages",
    headers={
        "x-api-key": os.environ['KHABEER_API_KEY'],
        "Content-Type": "application/json",
        "anthropic-version": "2023-06-01",
    },
    json={
    "model": "claude-sonnet-5.5",
    "max_tokens": 512,
    "messages": [
        {
            "role": "user",
            "content": "Write a product idea in one sentence."
        }
    ]
},
    stream=False,
    timeout=60,
)

response.raise_for_status()
print(response.json())
```

## Anthropic's OpenAI endpoint or 5abeer's?

Anthropic runs its own OpenAI-compatible endpoint. Checked on 2026-11-03 in Anthropic's docs and ours:

|  | Anthropic's OpenAI endpoint | 5abeer |
| --- | --- | --- |
| Base URL | `https://api.anthropic.com/v1/` | `https://api.5abeer.ai/v1` |
| Key | An Anthropic API key | A 5abeer key for Claude models |
| Model id | Anthropic's ids, with a hyphen in the version | Our catalog's ids, such as `claude-sonnet-5.5` |
| `response_format` | Only `json_schema` with `strict: true` | Ignored: use a tool with a schema |
| `reasoning_effort` | Ignored | Supported |
| `stop` | Works for non-whitespace text | Ignored: use `stop_sequences` in Messages |
| Prompt caching | Not in this format | Not in this format; use `cache_control` in Messages |

Anthropic calls its layer "primarily intended to test and compare model capabilities" and points to its native API for the full feature set. The same holds here: the OpenAI SDK is the quick way in, and Messages is where Claude's own settings live.

## What does the move cost?

A 5abeer API key pays per token from your balance, and every model priced per token costs less than its official price. As an example, 3,000 requests to `claude-sonnet-5.5`, each with about 2,000 input tokens and 500 output tokens, thinking included:

Example: 6,000,000 input tokens and 1,500,000 output tokens on Claude Sonnet 5.5 (`claude-sonnet-5.5`) cost $8.10 at our price against $27.00 at the official price (70% lower). Official price checked October 19, 2026.

Live prices are on [Claude API](https://5abeer.ai/en/api/claude), and [What is a token](https://5abeer.ai/en/guides/what-is-a-token) shows how a request turns into tokens. Each key can have a spend limit, an expiry date and an IP allow-list, as in [Control every key](https://5abeer.ai/en/docs/start/keys.md#controls).

Plans are for the supported apps; to use the models from your own code, create an API key. For Claude in the terminal rather than in your code, see [Claude Code](https://5abeer.ai/en/docs/apps/claude-code.md).

## FAQ

### Can I keep my OpenAI code and call Claude?

Yes. Point the OpenAI SDK at `https://api.5abeer.ai/v1`, use a key for Claude models and pass a Claude model id. Chat Completions and Responses both work.

### When is the Anthropic SDK worth the rewrite?

When you need `cache_control`, thinking settings or `output_config`. For plain chat and tool calls, the three settings are enough, and one code path can still call several providers.

### Why does Claude return 400 for a request that worked on GPT?

The usual cause is a sampling setting or a forced tool call. Anthropic's newest models reject a non-default `temperature` or `top_p`, and a `tool_choice` that forces a tool. Remove them and try again.

### Do I need a new key for Claude?

Yes. A 5abeer key works with one provider's models, so a key for GPT models can't call Claude. Create one for Claude models; all your keys draw on the same balance.

### Should I rewrite my prompts?

Anthropic suggests reviewing them: a prompt tuned closely for GPT models may need changes to get the best from Claude. Try your main prompts with the test request above before you move the rest.

## Sources

Checked on 2026-11-03:

- Anthropic: [OpenAI SDK compatibility](https://platform.claude.com/docs/en/cli-sdks-libraries/libraries/openai-sdk)
- Anthropic: [Using the Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages)
- Anthropic: [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools)
- Anthropic: [Thinking](https://platform.claude.com/docs/en/build-with-claude/thinking)
- Anthropic: [anthropic-sdk-python library](https://github.com/anthropics/anthropic-sdk-python)
- OpenAI: [openai-python library](https://github.com/openai/openai-python) and [openai-node library](https://github.com/openai/openai-node)

> 5abeer is an independent service, not affiliated with OpenAI, Anthropic or Google.

## Get started

Create a key for Claude models, set it as `KHABEER_API_KEY`, run the test request above, then change the base URL and the model id across your project.
