01Что это
API к моделям — продукт xWires для команд разработки. Компания подключает его по заявке, а дальше админ компании сам создаёт в кабинете ключ каждой команде или сервису, задаёт лимиты трат и видит расход по каждому запросу. Запросы идут через шлюз xWires и списываются с баланса компании по её ценам. API совместим с API OpenAI: готовый код и инструменты работают, если поменять адрес, ключ и модель.
Вызвать модель можно двумя способами:
| Chat Completions | Responses | |
|---|---|---|
| Путь | Chat Completions/chat/completions | Responses/responses |
| В SDK OpenAI | Chat Completionsclient.chat.completions.create() | Responsesclient.responses.create() |
| Что отправляете | Chat Completionsmessages: сообщения с ролями | Responsesinput: сообщения или блоки |
| Поиск в интернете | Chat CompletionsТолько своими функциями (function calling) на вашей стороне | ResponsesВстроенный: инструмент web_search |
| Когда брать | Chat CompletionsЧат, генерация текста и кода, перенос готового кода с OpenAI | ResponsesОтветы со свежими данными из интернета, агенты |
Какие модели xWires продаёт сейчас, сколько они принимают и что умеют — в раздел 4.
Это не API управления компанией. Тот нужен кадровой и учётной системам для подписок ChatGPT, у него свой адрес и свои ключи, и он описан в отдельном руководстве. Ключ API управления к моделям не подходит, и наоборот.
02Быстрый старт
- Админ компании открывает в xWires «Кабинет компании → API к моделям» и создаёт ключ команды: ключ показывается один раз. ID моделей — в таблице «Модели и цены» там же. Если API ещё не подключено, оставьте заявку на странице «API к моделям».
- Положите адрес, ключ и ID модели в переменные окружения сервера:
export XWIRES_BASE_URL="https://api.xwires.ai/v1" export XWIRES_API_KEY="ключ вашей команды из кабинета" export XWIRES_MODEL="ID модели из кабинета"
- Отправьте первый запрос:
curl "$XWIRES_BASE_URL/chat/completions" \
-H "Authorization: Bearer $XWIRES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "'"$XWIRES_MODEL"'",
"messages": [
{"role": "system", "content": "Ты — помощник разработчика. Отвечай кратко."},
{"role": "user", "content": "Привет! Что ты умеешь?"}
]
}'{
"id": "0217923680000000000000000000000000000000000000000000",
"object": "chat.completion",
"created": 1792368000,
"model": "<ID модели>",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Помогаю с разработкой: пишу, объясняю и отлаживаю код, отвечаю на технические вопросы.",
"reasoning_content": "The user greets me and asks what I can do, in one short answer…"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 131,
"completion_tokens": 184,
"total_tokens": 315,
"prompt_tokens_details": {
"cached_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 124
}
}
}В агентах для программирования, LangChain и других инструментах выберите OpenAI-совместимого провайдера (OpenAI compatible) и укажите те же три значения: адрес API (base URL), ключ и ID модели. Код на Python и Node.js — в раздел 5.
03Адрес, ключ и модель
Адрес API
https://api.xwires.ai/v1 — базовый адрес, к которому добавляется путь: /chat/completions или /responses. Все запросы — HTTPS POST с телом JSON и заголовком Content-Type: application/json; список моделей — GET /models, одна модель по ID — GET /models/{model}. В SDK OpenAI адрес передаётся как base_url (Python) или baseURL (Node.js).
Описание API в формате OpenAPI 3.0 — openapi.json: адрес, ключ, пути, их поля и ошибки шлюза. Его берут генераторы клиентов, Postman и Insomnia.
Ключ
Ключ идёт в заголовке Authorization: Bearer каждого запроса и начинается с sk-xw-. Ключи создаёт админ компании в «Кабинет компании → API к моделям»: у каждой команды или сервиса свой, по нему виден расход и задаётся лимит трат. Целиком ключ показывается один раз, при создании; дальше в кабинете видно только его начало.
- Ключ живёт только на сервере: в переменной окружения или хранилище секретов. В код, репозиторий, браузер и мобильное приложение он не попадает: из них его легко достать, и тратить баланс компании сможет кто угодно.
- Ключ потерян, попал в репозиторий или мог оказаться у посторонних — админ заменяет его в кабинете: имя, лимиты и расход остаются, а старый ключ перестаёт работать через несколько секунд. Ключ, который больше не нужен, админ отзывает.
ID модели
Модель выбирается полем model в каждом запросе. ID модели — её имя у производителя, например claude-opus-5-5; тот же ID приходит в поле model ответа. ID моделей — в кабинете, в таблице «Модели и цены», вместе с ценами компании за 1 млн токенов. Модели, открытые ключу, отдаёт GET /models с ключом команды: у каждой контекст и предел вывода, цен в ответе нет. В примерах этого руководства ID берётся из переменной XWIRES_MODEL.
curl "$XWIRES_BASE_URL/models" \ -H "Authorization: Bearer $XWIRES_API_KEY"
Рассуждение перед ответом
Модели с рассуждением (раздел 4) сначала рассуждают, потом отвечают. Рассуждение приходит вместе с ответом: в Chat Completions — в message.reasoning (у части моделей — в message.reasoning_content), в Responses — отдельным элементом reasoning перед сообщением. Показывать его пользователю не обязательно, а язык у него бывает другим, чем у ответа. Токены рассуждения входят в токены ответа (completion_tokens, из них reasoning_tokens) и в предел max_tokens: если предел мал, модель потратит его на рассуждение и вернёт пустой ответ с finish_reason: length. Поэтому max_tokens не задавайте вовсе или оставляйте запас на сотни токенов.
04Модели
Моделей в продаже сейчас нет: они появятся здесь и в каталоге на странице «API к моделям». Модели, открытые ключу, — в «Кабинет компании → API к моделям» и в ответе GET /models, а цены компании — только в кабинете.
05Chat Completions
POST на адрес API с путём /chat/completions — тот же запрос, что у OpenAI, поэтому подходят официальные SDK и готовый код. Пример на curl — в раздел 2.
Параметры
| Параметр | Тип | Обязательный | Что значит |
|---|---|---|---|
model | Типстрока | Обязательныйда | Что значитID модели из кабинета |
messages | Типмассив | Обязательныйда | Что значитДиалог по порядку: сообщения с role (system, user или assistant) и content |
stream | Типлогический | Обязательныйнет | Что значитtrue — ответ приходит по частям, событиями SSE, по мере того как модель его пишет. По умолчанию false |
temperature | Типчисло | Обязательныйнет | Что значитСлучайность ответа, обычно от 0 до 1: чем выше, тем разнообразнее ответ, чем ниже, тем предсказуемее |
max_tokens | Типцелое | Обязательныйнет | Что значитПредел токенов ответа вместе с рассуждением модели: слишком малый оставит ответ пустым |
Python
Нужен официальный SDK OpenAI версии 1.0 или новее: pip install --upgrade "openai>=1.0".
import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ["XWIRES_BASE_URL"],
api_key=os.environ["XWIRES_API_KEY"],
timeout=120,
)
MODEL = os.environ["XWIRES_MODEL"]
messages = [
{"role": "system", "content": "Ты — помощник разработчика. Отвечай кратко."},
{"role": "user", "content": "Привет! Что ты умеешь?"},
]
# Ответ целиком
completion = client.chat.completions.create(model=MODEL, messages=messages)
print(completion.choices[0].message.content)
print(completion.usage)
# Ответ по частям, по мере того как модель его пишет
stream = client.chat.completions.create(model=MODEL, messages=messages, stream=True)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()Node.js
Официальный SDK OpenAI: npm install openai.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: process.env.XWIRES_BASE_URL,
apiKey: process.env.XWIRES_API_KEY,
timeout: 120_000,
});
const model = process.env.XWIRES_MODEL;
const messages = [
{ role: "system", content: "Ты — помощник разработчика. Отвечай кратко." },
{ role: "user", content: "Привет! Что ты умеешь?" },
];
// Ответ целиком
const completion = await client.chat.completions.create({ model, messages });
console.log(completion.choices[0].message.content);
console.log(completion.usage);
// Ответ по частям, по мере того как модель его пишет
const stream = await client.chat.completions.create({ model, messages, stream: true });
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");Ответ
choices[0].message.content— текст ответа,choices[0].message.reasoning— рассуждение модели перед ним.choices[0].finish_reason— почему модель остановилась:stop— ответ закончен,length— ответ с рассуждением дошёл доmax_tokens.usage— токены запроса (prompt_tokens, из них взятые из кэша —prompt_tokens_details.cached_tokens), ответа (completion_tokens, из них рассуждение —completion_tokens_details.reasoning_tokens) и всего (total_tokens).- В ответе по частям сначала идут куски с
choices[0].delta.reasoning, потом с текстом ответа вchoices[0].delta.content; примеры выше печатают только текст. С"stream_options": {"include_usage": true}в запросеusageпридёт в последнем куске, без него — не придёт. Этот кусок может нести иchoicesсfinish_reason: беритеusageиз куска, где оно неnull, а не по пустомуchoices; кусок безchoicesпропускайте, как в примерах.
06Responses и веб-поиск
POST на адрес API с путём /responses — интерфейс со встроенными инструментами на стороне сервера. Главный из них — поиск в интернете, web_search: модель сама ищет и отвечает со свежими данными, без вашего поискового API и цикла вызова функций.
Поиск есть не у каждой модели: у тех, где он есть, в кабинете в таблице «Модели и цены» стоит цена вызова поиска и предел поисков на запрос. Каждый поиск списывается по этой цене, а его результаты входят в токены ввода. Запрос с поиском к модели без него получает 400 unsupported_feature и ничего не стоит.
Из встроенных инструментов шлюз пропускает только поиск. Интерпретатор кода, поиск по файлам, генерация картинок, MCP-серверы и загрузка страниц получают 400 unsupported_feature у любой модели и тоже ничего не стоят. Свои функции, которые выполняет ваш код, работают как обычно, кроме загружаемых по требованию (defer_loading).
Параметры
| Параметр | Тип | Обязательный | Что значит |
|---|---|---|---|
model | Типстрока | Обязательныйда | Что значитID модели из кабинета |
input | Типмассив | Обязательныйда | Что значитСообщения с role и content. content — строка или список блоков, например {"type": "input_text", "text": "…"} |
tools | Типмассив | Обязательныйнет | Что значитВстроенные инструменты. {"type": "web_search"} включает поиск в интернете |
max_tool_calls | Типцелое | Обязательныйнет | Что значитСколько поисков модель может сделать за запрос. Больше предела модели не выйдет: шлюз ставит его сам |
stream | Типлогический | Обязательныйнет | Что значитtrue — ответ по частям, событиями SSE. По умолчанию false |
Python
Тот же SDK OpenAI: в input можно передать сообщения со строкой в content. В output ответа приходят рассуждение (элемент reasoning) и сообщение с блоком output_text, а свойство SDK output_text собирает из них только текст ответа.
import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ["XWIRES_BASE_URL"],
api_key=os.environ["XWIRES_API_KEY"],
timeout=120,
)
response = client.responses.create(
model=os.environ["XWIRES_MODEL"],
input=[{"role": "user", "content": "Какие новости в мире ИИ за эту неделю?"}],
tools=[{"type": "web_search"}],
max_tool_calls=2,
)
print(response.output_text)curl, ответ по частям
В REST-запросе content — список блоков input_text. С stream: true ответ приходит событиями SSE.
curl "$XWIRES_BASE_URL/responses" \
-H "Authorization: Bearer $XWIRES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "'"$XWIRES_MODEL"'",
"stream": true,
"tools": [{"type": "web_search"}],
"max_tool_calls": 3,
"input": [
{
"role": "user",
"content": [{"type": "input_text", "text": "Какие новости в мире ИИ за эту неделю?"}]
}
]
}'Поиски каждого запроса видны в «Расходе токенов» в кабинете, вместе с его токенами и списанной суммой.
07Ошибки
Ошибка приходит с кодом HTTP и телом, как у OpenAI: {"error": {"code": "…", "message": "…", "type": "…"}}. SDK OpenAI превращают её в исключение с этим кодом. Отказ шлюза — нет денег, лимит, ключ — приходит до модели, ничего не стоит и помечен заголовком X-Xwires-Refusal с его кодом. Ошибка самой модели приходит с её кодом и текстом, без этого заголовка, даже если код такой же, как у отказа шлюза. У каждого ответа есть заголовок X-Request-Id: если пишете нам, приложите его.
| Код | Что случилось | Что делать |
|---|---|---|
| 400 Bad Request | Что случилосьНеверный JSON, нет обязательного поля или недопустимое значение; unsupported_feature — модель не умеет того, что просит запрос, например поиск, или шлюз не пропускает встроенный инструмент | Что делатьИсправьте запрос: повтор не поможет |
| 401 Unauthorized | Что случилосьinvalid_api_key — нет ключа, ключ неверный, заменён или отозван; expired_api_key — срок ключа вышел | Что делатьПроверьте заголовок Authorization: Bearer и ключ. Новый ключ админ компании создаёт в кабинете |
| 402 Payment Required | Что случилосьinsufficient_balance — на балансе компании не осталось денег | Что делатьБаланс пополняет xWires по оплаченному счёту. Повтор не поможет, пока баланса нет |
| 403 Forbidden | Что случилосьspend_limit_exceeded — исчерпан лимит трат ключа или компании за день или месяц; model_not_allowed — модель не открыта ключу или компании; account_disabled — шлюз для компании закрыт | Что делатьЛимиты меняет админ в кабинете; дневной лимит обновится в 00:00 UTC, месячный — 1-го числа. Если закрыт шлюз, напишите на support@xwires.ai |
| 404 Not Found | Что случилосьmodel_not_found — такой модели нет или она больше не продаётся; либо ID с провайдером, как goat-claude/claude-opus-5-5, — тогда новый ID в сообщении ошибки; либо ошибка в адресе или пути | Что делатьСверьте ID модели с кабинетом или GET /models |
| 429 Too Many Requests | Что случилосьrate_limit_exceeded — слишком много запросов или токенов в минуту на ключ; too_many_concurrent_requests — у компании слишком много запросов сразу, и за 10 секунд место не освободилось | Что делатьПовторите через столько секунд, сколько в заголовке Retry-After, или с нарастающей паузой (раздел 8) |
| 500, 502, 503 | Что случилосьСбой или перегрузка у модели; billing_unavailable — шлюз не может проверить баланс | Что делатьПовторите с нарастающей паузой |
08В продакшене
- Таймаут. Для ответа целиком начните с 60–120 секунд и подстройте по своим запросам; модели с рассуждением на сложных задачах думают дольше. Длинный ответ лучше получать по частям (
stream): первые слова придут сразу, а соединение не простаивает. - Повторы. 429, 500, 503 и обрыв соединения повторяйте с нарастающей паузой — 1, 2, 4, 8 секунд с небольшим случайным разбросом — и ограничьте число попыток. Если в ответе 429 есть
Retry-After, ждите столько секунд, сколько в нём. 400, 401, 402, 403 и 404 повтором не исправить. SDK OpenAI повторяют такие ошибки сами, по умолчанию дважды (max_retriesв Python,maxRetriesв Node.js). - Учёт. Пишите
usageиз ответа в свои логи вместе с командой или сервисом иX-Request-Id: по нему запрос находится в «Расходе токенов» в кабинете. В ответе по частямusageпридёт в последнем куске, если в запросе есть"stream_options": {"include_usage": true}. - Кэш. Агенты раз за разом отправляют одно и то же начало запроса: системный промпт, код, историю. Держите это начало неизменным, а то, что меняется, ставьте в конец: повторяющееся начало модель берёт из кэша, и такие токены стоят дешевле нового ввода. Кэш включается сам, а сколько токенов запроса пришло из него, показывает
usage.prompt_tokens_details.cached_tokens.
09Расход и лимиты
Каждый запрос списывается с баланса компании по её цене за 1 млн токенов: ввод, чтение из кэша, запись в кэш и вывод, рассуждение — как вывод, поиск — за вызов. Сумма округляется вверх до миллионной доли доллара. Баланс общий с картами компании и пополняется по оплаченному счёту.
Админ видит расход в «Кабинет компании → Расход токенов» через несколько секунд после ответа: траты за месяц в долларах и прогноз, баланс, по моделям и ключам и каждый запрос. Дни и месяцы считаются по UTC.
- Лимиты трат. Админ задаёт их в «Кабинет компании → API к моделям»: на компанию или ключ, на все модели или одну, за день и за месяц. Запрос сверх лимита получает 403
spend_limit_exceeded. - Баланс. Запрос пускается, пока на балансе есть деньги, и дорабатывает до конца, даже если уводит баланс в минус. Пока баланс не больше нуля, запросы получают 402
insufficient_balance. - Обрыв. Если клиент ушёл посреди ответа, запрос списывается по оценке: ввод — по размеру запроса, вывод — по тому, что модель успела написать.
- Запросы в минуту и одновременно. Когда у компании идёт столько запросов, сколько ей можно одновременно, следующий ждёт свободного места до 10 секунд и только потом получает 429
too_many_concurrent_requests. Если упираетесь в 429 или ждёте рост нагрузки, напишите на support@xwires.ai: сколько запросов в минуту и сколько одновременных запросов нужно в пике.
10Безопасность
- Ключ — секрет компании: только на сервере, у каждой команды свой (раздел 3).
- Ключ мог оказаться у посторонних — админ сразу заменяет или отзывает его в кабинете, а лимит трат на ключ ограничит ущерб, пока этого не заметили.
- Вопросы о подключении, ключах и лимитах — на support@xwires.ai.