OpenAI API и Anthropic API: разница форматов и что выбрать для своего кода
Два формата запросов решают одну задачу, но различаются в мелочах, которые ломают код при переносе. Сравниваем структуру, инструменты, стриминг, кэш и заголовки.
6 мин · · API-ключи · сравнение
Оба формата отправляют модели сообщения и получают ответ, поэтому кажутся взаимозаменяемыми. Но при переносе кода с одного на другой вылезают мелочи: где лежит system, обязателен ли max_tokens, как выглядит ответ и как читать вызовы инструментов. Ниже разбор OpenAI API vs Anthropic API: сравнение /v1/chat/completions и /v1/messages, примеры кода для каждого и объяснение, какой формат выгоднее при работе через шлюз. В Zukko любую модель можно вызвать в любом формате, поэтому выбор определяется кодом и расходами, а не тем, чья это модель.
Быстрое сравнение
| Что | OpenAI: /v1/chat/completions | Anthropic: /v1/messages |
|---|---|---|
| Заголовок ключа | Authorization: Bearer | x-api-key (шлюз принимает и Bearer) |
| Версия API | не нужна | anthropic-version: 2023-06-01 (SDK ставит сам) |
| System-инструкция | сообщение с ролью system в messages | отдельное поле system верхнего уровня |
max_tokens | необязателен | обязателен |
| Ответ | choices[0].message.content | список блоков content, текст в content[0].text |
| Расход токенов | usage.prompt_tokens, completion_tokens | usage.input_tokens, output_tokens и поля кэша |
| Инструменты | tools с type: "function", ответ в tool_calls | tools с input_schema, ответ блоком tool_use |
| Стриминг | чанки delta, завершение [DONE] | типизированные события (message_start, content_block_delta, message_stop) |
| Кэш у Claude | нет разбивки: весь вход по цене входа | есть: чтение и запись кэша учитываются отдельно |
| Base URL в SDK | https://api.zukko.pro/v1 | https://api.zukko.pro (SDK добавит /v1) |
Ниже по пунктам.
Структура запроса и ответа
OpenAI-формат
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1", api_key=os.environ["ZUKKO_API_KEY"])
resp = client.chat.completions.create(
model="claude-sonnet-5-5",
messages=[
{"role": "system", "content": "Отвечай кратко."},
{"role": "user", "content": "Что такое токен?"},
],
max_tokens=300,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)Anthropic-формат
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.zukko.pro", # без /v1: SDK добавит сам
api_key=os.environ["ZUKKO_API_KEY"],
)
resp = client.messages.create(
model="claude-sonnet-5-5",
system="Отвечай кратко.",
messages=[{"role": "user", "content": "Что такое токен?"}],
max_tokens=300,
)
print(resp.content[0].text)
print(resp.usage.input_tokens, resp.usage.output_tokens)Заметные отличия: system вынесен из списка сообщений, max_tokens указывать обязательно, а ответ приходит как массив блоков. Блоков может быть несколько (например, текст и затем вызов инструмента), поэтому в реальном коде выбирайте блоки с type == "text", а не берите нулевой.
Для curl-запросов и описания полей смотрите документацию: chat/completions и messages.
Инструменты (function calling)
В OpenAI-формате инструмент описывается обёрткой function, а модель возвращает вызов в message.tool_calls; аргументы приходят JSON-строкой в function.arguments, которую нужно разобрать, а результат возвращается сообщением с ролью tool и tool_call_id:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Погода в городе",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
},
}]В Anthropic-формате схема лежит в input_schema, вызов приходит блоком tool_use с уже разобранным объектом input, а результат возвращается пользовательским сообщением с блоком tool_result (в нём tool_use_id):
tools = [{
"name": "get_weather",
"description": "Погода в городе",
"input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}]Полный цикл (вызов, выполнение функции, возврат результата) в обоих форматах занимает примерно одинаковый объём кода, отличаются только имена полей.
Стриминг
В OpenAI-формате сервер присылает чанки с choices[0].delta, в конце потока приходит чанк с usage и пустым choices. Поэтому в коде всегда проверяйте if chunk.choices: перед обращением к chunk.choices[0]:
stream = client.chat.completions.create(
model="claude-sonnet-5-5",
messages=[{"role": "user", "content": "Расскажи про кэш промптов"}],
stream=True,
max_tokens=500,
)
for chunk in stream:
if chunk.choices:
print(chunk.choices[0].delta.content or "", end="")В Anthropic SDK удобнее менеджер потока:
with client.messages.stream(
model="claude-sonnet-5-5",
messages=[{"role": "user", "content": "Расскажи про кэш промптов"}],
max_tokens=500,
) as stream:
for text in stream.text_stream:
print(text, end="")Под капотом это типизированные события, и вам не нужно разбирать их самому. Формат событий описан в разделе документации по стримингу.
Кэш и стоимость: главное практическое отличие
Здесь формат влияет на деньги. По правилам Zukko:
- для Claude в Anthropic-формате шлюз видит чтение и запись кэша (поля
cache_read_input_tokensиcache_creation_input_tokens) и оплачивает их по своим ценам; - для Claude в OpenAI-формате разбивки на кэш нет, и весь вход идёт по цене входа;
- для GPT чтение кэша видно в обоих форматах (
prompt_tokens_details.cached_tokens).
Пример. Агентный шаг на claude-sonnet-5-5 (вход $0.4, чтение кэша $0.05 за 1M токенов, цены на 18 октября 2026) с префиксом 50 000 токенов, который целиком читается из кэша. Считаем только этот префикс, без выхода и новых токенов:
/v1/messages: 50 000 × 0.05 / 1 000 000 = $0.0025 за запрос;/v1/chat/completions: 50 000 × 0.4 / 1 000 000 = $0.02 за запрос.
На 100 запросах получается $0.25 против $2.00. Это верхняя оценка, предполагающая, что кэш попал на каждом запросе (первая запись кэша стоит отдельно, у Sonnet 5.5 это $0.5 за 1M). Но направление очевидно: для длинных повторяющихся префиксов (агенты, чат с большим системным промптом) Claude выгоднее вызывать через Anthropic-формат. Подробнее — в статье Claude через /v1/messages или /v1/chat/completions: что будет с кэшем.
Чтобы кэш в Anthropic-формате работал, нужно, чтобы клиент помечал кэшируемую часть. Claude Code и многие агенты делают это сами. В собственном коде это выглядит так:
resp = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=300,
system=[{
"type": "text",
"text": LONG_INSTRUCTION, # длинный стабильный префикс
"cache_control": {"type": "ephemeral"}, # пометка для кэша
}],
messages=[{"role": "user", "content": "Вопрос по инструкции"}],
)
print(resp.usage.cache_creation_input_tokens, resp.usage.cache_read_input_tokens)Минимальная длина кэшируемого префикса зависит от модели, смотрите документацию Anthropic. Общая формула: свежий вход × цена входа + чтение кэша × цена чтения + запись кэша × цена записи + выход × цена выхода.
Другие модели через любой формат
Через шлюз модели не привязаны к «своему» формату. GPT, Gemini, DeepSeek, Qwen и GLM можно вызывать и через /v1/chat/completions, и через /v1/messages: шлюз переводит запрос. Например, glm-5.3-flash в Anthropic-формате работает, а шлюз сам конвертирует запрос и ответ. Но у разных моделей разные нюансы: не все поддерживают одинаковый набор параметров, поэтому проверяйте поведение на своём коде. Актуальный список моделей и цены: GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете.
Что выбрать
| Ситуация | Формат |
|---|---|
| Уже есть код на OpenAI SDK, много моделей разных вендоров | /v1/chat/completions |
| Основная модель Claude, длинные повторяющиеся префиксы, агенты | /v1/messages |
| Claude Code, Anthropic SDK, любой клиент «режима Anthropic» | /v1/messages (клиент сам) |
| Cursor, Cline в режиме OpenAI, LangChain | /v1/chat/completions |
| Новый проект без предпочтений | OpenAI-формат как самый распространённый; для Claude с кэшем Anthropic |
Если вы выбираете провайдера в IDE, пользуйтесь страницей «Выбор провайдера» в документации Zukko: Anthropic с адресом https://api.zukko.pro или OpenAI-совместимый с /v1. Ключ в обоих случаях один и тот же. Третий эндпоинт, /v1/responses, тоже доступен, но это отдельная тема.
Переход с одного на другой: чек-лист
- Поменяйте base URL: с
/v1для OpenAI SDK, без/v1для Anthropic SDK. - Вынесите
systemизmessagesили обратно. - Добавьте
max_tokensдля Anthropic-формата. - Переделайте разбор ответа и поля
usage. - Перепишите описания инструментов и обработку результатов.
- Проверьте стриминг, включая последний чанк с
usage. - Прогоните тесты на своих промптах и сверьте расход в кабинете.
Ключ для обоих форматов один, создаётся в кабинете Zukko.
Частые вопросы
Можно ли вызвать Claude через OpenAI SDK?
Да, через /v1/chat/completions с адресом https://api.zukko.pro/v1. Работает, но кэш Claude в этом формате не разбивается и весь вход идёт по цене входа.
Можно ли вызвать GPT через Anthropic SDK?
Шлюз позволяет вызывать любую модель в любом формате, но проверяйте поведение конкретной модели на вашем коде. Для GPT проще и привычнее OpenAI-формат.
Чем отличаются заголовки авторизации?
OpenAI использует Authorization: Bearer, Anthropic x-api-key и anthropic-version. Шлюз Zukko принимает ключ в любом из двух заголовков.
Почему в Anthropic-формате обязателен max_tokens?
Так устроен формат Anthropic: поле обязательное. В OpenAI-формате его можно не указывать, но для контроля расхода рекомендуем задавать лимит в обоих.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Gemini API vs Vertex API: в чём разница и что выбрать разработчикуДве «двери» к Gemini от Google устроены по-разному: ключ из AI Studio или проект в Google Cloud. Сравниваем их и показываем третий путь через шлюз.
- OpenRouter: что это за сервис и кому он подходит в 2026 годуOpenRouter даёт один API и один баланс на множество моделей разных поставщиков. Разбираем, как он устроен, какие у него комиссии и ограничения, и в каких случаях удобнее другой шлюз.
- Ключи OpenAI API в открытом доступе: почему ими нельзя пользоватьсяОткуда берутся «бесплатные» ключи на GitHub, почему пользоваться ими опасно и для вас, и для владельца, и как защитить собственный ключ от такой же утечки.
- Ошибки GLM API: почему не работает запрос и как это исправитьНеверный id модели, переполненный контекст 128K, ключ, баланс, лимиты: типичные причины ошибок GLM API и быстрый способ найти свою.