Перейти к основному содержимому

Примеры подключения и использования

примечание

Базовый URL и API-ключ выдаются при подключении. Во всех примерах они подставляются через переменные BASE_URL и API_KEY — замените их на выданные вам значения.

Общие сведения о подключении — в разделе Инструкция по подключению, полный список маршрутов и коэффициентов — в разделе Доступные модели (Nexuses).

export API_KEY="ваш_API_ключ"
export BASE_URL="https://<адрес, выданный при подключении>/v1"

Общие правила

Во всех запросах обязательны заголовки:

ЗаголовокНазначение
Authorization: Bearer <ключ>API-ключ, выдаётся при подключении
X-AIN-USERNAMEИмя пользователя — для аналитики потребления
X-AIN-SOURCEIDИдентификатор вашей системы — для аналитики потребления

Маршрут всегда передаётся в поле model в теле запроса. Шлюз определяет модель именно по этому полю, а не по URL.

Суффикс маршрута подсказывает, по какому пути его вызывать. Маршрут, оканчивающийся на .chat_completions, отправляется на POST /chat/completions; на .responses — на POST /responses; на .audio_transcriptions — на путь транскрипции и так далее. Если отправить маршрут не на тот путь, шлюз вернёт ошибку. Точный путь по каждому маршруту указан в колонке API-путь каталога моделей.

Список маршрутов, доступных вашему ключу:

curl -sS "$BASE_URL/models" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы"

Ответ содержит только те маршруты, которые разрешены вашей лицензией. Если нужного маршрута в списке нет — обратитесь в поддержку.

Chat Completions

Базовый сценарий для всех текстовых моделей. API совместим с OpenAI.

Python (httpx)

import httpx

BASE_URL = "https://<адрес, выданный при подключении>/v1"
API_KEY = "ваш_API_ключ"

headers = {
"Authorization": f"Bearer {API_KEY}",
"X-AIN-USERNAME": "ваш_логин",
"X-AIN-SOURCEID": "название_системы",
}

client = httpx.Client(base_url=BASE_URL, headers=headers, timeout=120.0)

resp = client.post("/chat/completions", json={
"model": "opr.deepseek4_pro.chat_completions",
"messages": [{"role": "user", "content": "Привет! Расскажи о себе."}],
"max_tokens": 512,
"temperature": 0.7,
})
resp.raise_for_status()
print(resp.json()["choices"][0]["message"]["content"])

Python (openai SDK)

from openai import OpenAI

client = OpenAI(
base_url="https://<адрес, выданный при подключении>/v1",
api_key="ваш_API_ключ",
default_headers={
"X-AIN-USERNAME": "ваш_логин",
"X-AIN-SOURCEID": "название_системы",
},
)

response = client.chat.completions.create(
model="opr.claude-sonnet4.6.chat_completions",
messages=[{"role": "user", "content": "Напиши краткое резюме статьи..."}],
)
print(response.choices[0].message.content)

Потоковый ответ

stream = client.chat.completions.create(
model="opr.claude-sonnet4.6.chat_completions",
messages=[{"role": "user", "content": "Расскажи длинную историю."}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)

Protected-эндпоинты

Маршруты с суффиксом .protected вызываются точно так же — меняется только значение model. Запрос дополнительно проходит через слой фильтрации контента.

resp = client.post("/chat/completions", json={
"model": "opr.claude-sonnet4.6.chat_completions.protected",
"messages": [{"role": "user", "content": "Текст с персональными данными..."}],
})

Генерация изображений

Изображения генерируются по двум разным протоколам. Выбор протокола определяется суффиксом маршрута.

Суффикс маршрутаПротоколAPI-путь
.chat_completionsChat Completions/chat/completions
.imagesImage API/images

Протокол 1: через Chat Completions

Так работают маршруты opr.nano-banana-2.chat_completions и opr.nano-banana-2-lite.chat_completions. Изображение возвращается внутри обычного chat-ответа.

Обязательное поле — modalities со значением ["image", "text"]. Без него модель вернёт только текст.

import base64
import httpx

BASE_URL = "https://<адрес, выданный при подключении>/v1"
API_KEY = "ваш_API_ключ"

headers = {
"Authorization": f"Bearer {API_KEY}",
"X-AIN-USERNAME": "ваш_логин",
"X-AIN-SOURCEID": "название_системы",
}

resp = httpx.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json={
"model": "opr.nano-banana-2.chat_completions",
"messages": [{"role": "user", "content": "рыжий кот на диване, фотореализм"}],
"modalities": ["image", "text"],
"image_config": {"aspect_ratio": "16:9"},
},
timeout=300.0,
)
resp.raise_for_status()

# Изображение приходит как data-URL внутри message.images
url = resp.json()["choices"][0]["message"]["images"][0]["image_url"]["url"]
header, b64 = url.split(",", 1)
with open("output.png", "wb") as f:
f.write(base64.b64decode(b64))

Допустимые значения aspect_ratio: "1:1", "16:9", "9:16".

Протокол 2: через Image API

Так работают маршруты opr.flux-2-pro.images, opr.flux-2-klein-4b.images, opr.seedream-4.5.images, opr.recraft-v4.images, opr.recraft-v4-vector.images, opr.gpt-image-2.images. Используется отдельный путь /images, поле prompt вместо messages.

curl -sS -X POST "$BASE_URL/images" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-H "Content-Type: application/json" \
-d '{
"model": "opr.flux-2-pro.images",
"prompt": "рыжий кот на диване, фотореализм",
"aspect_ratio": "16:9",
"output_format": "png"
}'

Ответ:

{
"created": 1748372400,
"data": [
{
"b64_json": "<base64>",
"media_type": "image/png"
}
],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 4175,
"total_tokens": 4175
}
}

Извлечение результата:

import base64
import httpx

resp = httpx.post(
f"{BASE_URL}/images",
headers=headers,
json={
"model": "opr.flux-2-pro.images",
"prompt": "рыжий кот на диване, фотореализм",
"aspect_ratio": "16:9",
"output_format": "png",
},
timeout=300.0,
)
resp.raise_for_status()

item = resp.json()["data"][0]
with open("output.png", "wb") as f:
f.write(base64.b64decode(item["b64_json"]))
print(item["media_type"])
примечание

Изображение возвращается внутри JSON в base64, поэтому ответ может весить 3–4 МБ. Ставьте таймаут не меньше 300 секунд.

Генерация видео

Видео генерируется асинхронно, в три шага, и это отдельный протокол — не Chat Completions и не Image API.

ШагМетод и путьМаршрут в поле model
1. Отправка заданияPOST /videosopr.flux-3-video.videos
2. Опрос статусаGET /videos/{job_id}opr.flux-3-video.videos_status
3. СкачиваниеGET /videos/{job_id}/contentopr.flux-3-video.videos_content
warning

GET-запросы к видео обязаны содержать JSON-тело с полем model.

Шлюз определяет маршрут по телу запроса, а не по URL. Поэтому на шагах 2 и 3 нужны и заголовок Content-Type: application/json, и тело с указанием маршрута. Без них запрос завершится ошибкой:

  • нет Content-Type{"error": "unknown content-type"}
  • есть Content-Type, но нет тела → unexpected end of JSON input
  • тело пустое ({}) → {"error": "nexus not found"}

На каждом шаге указывается свой маршрут: .videos, .videos_status, .videos_content — это три разных nexus.

Шаг 1. Отправка задания

curl -sS -X POST "$BASE_URL/videos" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-H "Content-Type: application/json" \
-d '{
"model": "opr.flux-3-video.videos",
"prompt": "рыжий кот сидит на столе и спрыгивает на пол"
}'

Ответ:

{
"id": "RVVqCZGhmtQqUuLZ0RD8",
"status": "pending",
"generation_id": "gen-vid-1786110460-3Y9Z4eazO6HkLGfg6nYK"
}

Параметры генерации (длительность и разрешение) задаются платформой и входят в фиксированный тариф — см. Доступные модели (Nexuses). Указывать их в запросе не требуется.

Шаг 2. Опрос статуса

curl -sS -X GET "$BASE_URL/videos/$JOB_ID" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-H "Content-Type: application/json" \
-d '{"model": "opr.flux-3-video.videos_status"}'

Ответ при готовности:

{
"id": "RVVqCZGhmtQqUuLZ0RD8",
"status": "completed",
"usage": {"cost": 0.85}
}

Возможные значения status: pending, completed, failed.

warning

Вместе со статусом в ответе приходят служебные поля polling_url и unsigned_urls. Они ведут во внутреннюю инфраструктуру и вашим ключом AIaaS не открываются — вернётся 401 или 404. Скачивать результат нужно только через шлюз, шагом 3.

Шаг 3. Скачивание результата

curl -sS -o output.mp4 \
-X GET "$BASE_URL/videos/$JOB_ID/content?index=0" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-H "Content-Type: application/json" \
-d '{"model": "opr.flux-3-video.videos_content"}'

Ответ — бинарный mp4.

Полный цикл на Python

import time
import httpx

BASE_URL = "https://<адрес, выданный при подключении>/v1"
API_KEY = "ваш_API_ключ"
MODEL = "opr.flux-3-video"

headers = {
"Authorization": f"Bearer {API_KEY}",
"X-AIN-USERNAME": "ваш_логин",
"X-AIN-SOURCEID": "название_системы",
"Content-Type": "application/json",
}

with httpx.Client(base_url=BASE_URL, headers=headers, timeout=300.0) as client:
# 1. Отправка задания
resp = client.post("/videos", json={
"model": f"{MODEL}.videos",
"prompt": "рыжий кот сидит на столе и спрыгивает на пол",
})
resp.raise_for_status()
job_id = resp.json()["id"]
print("job:", job_id)

# 2. Опрос статуса — GET с телом запроса
for _ in range(40):
time.sleep(15)
resp = client.request(
"GET", f"/videos/{job_id}",
json={"model": f"{MODEL}.videos_status"},
)
resp.raise_for_status()
state = resp.json()
if state["status"] == "completed":
print("cost:", state.get("usage", {}).get("cost"))
break
if state["status"] == "failed":
raise RuntimeError(f"generation failed: {state}")
else:
raise TimeoutError("генерация не завершилась за отведённое время")

# 3. Скачивание
resp = client.request(
"GET", f"/videos/{job_id}/content",
params={"index": 0},
json={"model": f"{MODEL}.videos_content"},
)
resp.raise_for_status()
with open("output.mp4", "wb") as f:
f.write(resp.content)

Для Seedance замените значение MODEL на opr.seedance-2.0-fast — протокол идентичен.

примечание

Библиотека requests не позволяет отправить тело в GET-запросе штатным способом. Используйте httpx (client.request("GET", ..., json=...)) или curl.

Российские модели

Российские модели доступны через тот же шлюз и тот же ключ, что и остальные. Отдельные учётные записи у вендоров, сертификаты российских удостоверяющих центров, сервисные аккаунты и получение временных токенов — всё это остаётся на стороне шлюза. Со стороны вашего приложения российская модель отличается от любой другой только идентификатором маршрута.

Семейств два, и вызываются они по-разному:

СемействоПрефиксПутьФормат запроса
GigaChatsbr./chat/completionsКак у остальных chat-моделей
YandexGPT и Alice AIyndx./responsesОтличается, см. ниже

GigaChat

Ничего особенного: обычный запрос к /chat/completions.

resp = client.post("/chat/completions", json={
"model": "sbr.gigachat-pro.chat_completions",
"messages": [
{"role": "system", "content": "Отвечай кратко."},
{"role": "user", "content": "Привет!"},
],
})
answer = resp.json()["choices"][0]["message"]["content"]

YandexGPT и Alice AI

Эти маршруты работают по протоколу Responses — отсюда суффикс .responses в идентификаторе. Отличий от chat-моделей два, и оба ломают код, написанный под /chat/completions.

Первое: запрос. Вместо массива messages передаётся поле input.

resp = httpx.post(
f"{BASE_URL}/responses",
headers=headers,
json={
"model": "yndx.yandex-gpt-5.1-pro.responses",
"input": "Привет! Расскажи о себе.",
},
timeout=120.0,
)

Второе: ответ. Текст приходит не в choices, а в структуре протокола Responses — в массиве output. Привычное обращение resp.json()["choices"][0]["message"]["content"] здесь вернёт ошибку ключа.

примечание

При первой интеграции распечатайте ответ целиком и посмотрите фактическую структуру, а не разбирайте её вслепую:

import json
print(json.dumps(resp.json(), ensure_ascii=False, indent=2))

Это займёт минуту и избавит от догадок — формат протокола Responses отличается от chat-совместимого, и написанный под него разбор проще один раз увидеть, чем восстанавливать по ошибкам.

Роль системного сообщения. В input передаётся текст запроса. Если нужна системная инструкция, посмотрите в ответе на пробный запрос, какие поля принимает маршрут — набор параметров у протокола Responses свой.

OCR — распознавание текста из документов

Путь: POST /v1/v2/doc-to-text. Файл передаётся как multipart/form-data, маршрут — в поле формы model.

curl -sS -X POST "$BASE_URL/v2/doc-to-text" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-F "model=itg.ocr.v2.doc-to-text" \
-F "file=@document.pdf"

Ответ:

{
"text": "Извлечённый текст документа..."
}
import httpx

with open("document.pdf", "rb") as f:
resp = httpx.post(
f"{BASE_URL}/v2/doc-to-text",
headers=headers,
data={"model": "itg.ocr.v2.doc-to-text"},
files={"file": ("document.pdf", f, "application/pdf")},
timeout=120.0,
)
resp.raise_for_status()
print(resp.json()["text"])

Остальные маршруты OCR (process-document, vlm-process, custom-process-text и другие) вызываются так же — меняются путь и значение model.

Конвертация документов (Docling)

import httpx

with open("document.pdf", "rb") as f:
resp = httpx.post(
f"{BASE_URL}/v1/convert/file",
headers=headers,
data={"model": "itg.docling.convert_sync"},
files={"files": ("document.pdf", f, "application/pdf")},
timeout=300.0,
)
resp.raise_for_status()
print(resp.json())

Путь /v1/convert/file добавляется к базовому URL, который уже содержит /v1 — итоговый адрес выглядит как https://<адрес, выданный при подключении>/v1/v1/convert/file. Это не опечатка: первый /v1 — версия шлюза, второй — часть пути сервиса.

Транскрипция аудио (WhisperX)

curl -sS -X POST "$BASE_URL/audio/transcriptions" \
-H "Authorization: Bearer $API_KEY" \
-H "X-AIN-USERNAME: ваш_логин" \
-H "X-AIN-SOURCEID: название_системы" \
-F "model=itg.whisperx-prod.audio_transcriptions" \
-F "file=@recording.mp3"
import httpx

with open("recording.mp3", "rb") as f:
resp = httpx.post(
f"{BASE_URL}/audio/transcriptions",
headers=headers,
data={"model": "itg.whisperx-prod.audio_transcriptions"},
files={"file": ("recording.mp3", f, "audio/mpeg")},
timeout=300.0,
)
resp.raise_for_status()
print(resp.json()["text"])

Синтез речи (TTS)

import httpx

resp = httpx.post(
f"{BASE_URL}/audio/speech",
headers=headers,
json={
"model": "opr.gemini-3.1-flash-tts-preview.audio_speech",
"input": "Привет! Это тестовая генерация речи.",
"voice": "default",
},
timeout=120.0,
)
resp.raise_for_status()
with open("output.mp3", "wb") as f:
f.write(resp.content)

Ответ — бинарный аудиопоток. TTS тарифицируется по объёму сгенерированного аудио в байтах.

Embeddings и Reranker

Векторизация

resp = httpx.post(
f"{BASE_URL}/embeddings",
headers=headers,
json={
"model": "itg.qwen3-embedding-4b.embeddings",
"input": "Текст для векторизации",
},
timeout=60.0,
)
resp.raise_for_status()
print(resp.json()["data"][0]["embedding"][:8])

Внешние модели вызываются так же — достаточно поменять model на opr.voyage-4.embeddings, opr.bge-m3.embeddings или opr.gemini-embedding-2.embeddings. У всех embeddings-маршрутов тарифицируются только входящие токены.

Реранкинг

resp = httpx.post(
f"{BASE_URL}/rerank",
headers=headers,
json={
"model": "itg.qwen3-reranker-4b.rerank",
"query": "Поисковый запрос",
"documents": ["Документ 1", "Документ 2", "Документ 3"],
},
timeout=60.0,
)
resp.raise_for_status()
print(resp.json())

Обнаружение персональных данных (PII)

Сервис на базе GLiNER обнаруживает, маскирует и восстанавливает персональные данные в тексте.

ШагAPI-путьМаршрутОбязательные поля
Обнаружение/neritg.pii-detection.nertext
Маскирование/maskitg.pii-detection.masktext, entities
Восстановление/unmaskitg.pii-detection.unmasktext, mapping
warning

Маскирование выполняется в два шага. /mask принимает найденные сущности в поле entities — это результат вызова /ner. Вызов /mask без entities возвращает ошибку валидации 422.

Полный цикл

import httpx

BASE_URL = "https://<адрес, выданный при подключении>/v1"
API_KEY = "ваш_API_ключ"

headers = {
"Authorization": f"Bearer {API_KEY}",
"X-AIN-USERNAME": "ваш_логин",
"X-AIN-SOURCEID": "название_системы",
}

text = "Иванов Иван Иванович, телефон +7-999-123-45-67, email ivanov@example.com"

with httpx.Client(base_url=BASE_URL, headers=headers, timeout=60.0) as client:
# 1. Найти персональные данные
resp = client.post("/ner", json={
"model": "itg.pii-detection.ner",
"text": text,
})
resp.raise_for_status()
ner = resp.json()

if not ner["contains_sensible_info"]:
print("персональных данных не найдено")

# 2. Замаскировать — entities передаются из ответа /ner как есть
resp = client.post("/mask", json={
"model": "itg.pii-detection.mask",
"text": text,
"entities": ner["entities"],
})
resp.raise_for_status()
mask = resp.json()

masked_text = mask["text"]
mapping = mask["mapping"]

# ... здесь masked_text отправляется в модель ...

# 3. Восстановить значения — mapping передаётся из ответа /mask как есть
resp = client.post("/unmask", json={
"model": "itg.pii-detection.unmask",
"text": masked_text,
"mapping": mapping,
})
resp.raise_for_status()
restored = resp.json()

print(restored["text"])

Что возвращают ручки

/ner — найденные сущности с типом, границами и уверенностью:

{
"text": "Иванов Иван Иванович, телефон +7-999-123-45-67, email ivanov@example.com",
"entities": [
{"label": "PERSON_NAME", "start": 0, "end": 20, "score": 0.887},
{"label": "PHONE_NUMBER", "start": 30, "end": 46, "score": 0.973},
{"label": "EMAIL", "start": 54, "end": 72, "score": 0.985}
],
"contains_sensible_info": true,
"usage": {"input_tokens": 130}
}

/mask — обезличенный текст и словарь замен. Формат плейсхолдера — ***{N}_{LABEL}***:

{
"text": "***0_PERSON_NAME***, телефон ***1_PHONE_NUMBER***, email ***2_EMAIL***",
"mapping": {
"***0_PERSON_NAME***": "Иванов Иван Иванович",
"***1_PHONE_NUMBER***": "+7-999-123-45-67",
"***2_EMAIL***": "ivanov@example.com"
}
}

/unmask — текст с восстановленными значениями. Плейсхолдеры заменяются на исходные значения как есть: ***0_PERSON_NAME***Иванов Иван Иванович.

примечание

Поле usage возвращает только /ner. У /mask и /unmask его нет — это не ошибка: потребление всей цепочки считается по вызову детектора. У PII-маршрутов исходящие токены равны нулю, поскольку сервис возвращает разметку, а не сгенерированный текст.

Готовая прокси-обёртка, встраиваемая между приложением и моделью, описана в разделе Интеграция PII-прокси. Сравнение с protected-маршрутами — в разделе Protected-эндпоинты и защита персональных данных.

Перевод (DeepL)

resp = httpx.post(
f"{BASE_URL}/translate",
headers=headers,
json={
"model": "ain.deepl.translate",
"text": "Hello world",
"source_lang": "EN",
"target_lang": "RU",
},
timeout=60.0,
)
resp.raise_for_status()
print(resp.json())

Рекомендуемые таймауты

СценарийТаймаут
Chat Completions120 с
Генерация изображений300 с
Отправка задания на видео120 с
Скачивание видео300 с
OCR, Docling, транскрипция120–300 с в зависимости от размера файла
Embeddings, reranker, перевод60 с

Обработка ошибок

Полный разбор ответов шлюза с рекомендациями — в разделе Ошибки и диагностика.

ОтветПричина
{"error": "nexus \"...\" not found"}Маршрут не существует или недоступен вашему ключу. Проверьте список через GET /models.
{"error": "unknown content-type"}Не передан заголовок Content-Type: application/json.
unexpected end of JSON inputОтсутствует тело запроса. Актуально для GET-запросов к видео.
{"error": "invalid endpoint"}Маршрут вызван по неподходящему пути — например, видео-маршрут через /chat/completions.
HTTP 401Неверный или неактивный API-ключ.