Примеры подключения и использования
Базовый 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_completions | Chat Completions | /chat/completions |
.images | Image 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 /videos | opr.flux-3-video.videos |
| 2. Опрос статуса | GET /videos/{job_id} | opr.flux-3-video.videos_status |
| 3. Скачивание | GET /videos/{job_id}/content | opr.flux-3-video.videos_content |
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.
Вместе со статусом в ответе приходят служебные поля 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.
Российские модели
Российские модели доступны через тот же шлюз и тот же ключ, что и остальные. Отдельные учётные записи у вендоров, сертификаты российских удостоверяющих центров, сервисные аккаунты и получение временных токенов — всё это остаётся на стороне шлюза. Со стороны вашего приложения российская модель отличается от любой другой только идентификатором маршрута.
Семейств два, и вызываются они по-разному:
| Семейство | Префикс | Путь | Формат запроса |
|---|---|---|---|
| GigaChat | sbr. | /chat/completions | Как у остальных chat-моделей |
| YandexGPT и Alice AI | yndx. | /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-путь | Маршрут | Обязательные поля |
|---|---|---|---|
| Обнаружение | /ner | itg.pii-detection.ner | text |
| Маскирование | /mask | itg.pii-detection.mask | text, entities |
| Восстановление | /unmask | itg.pii-detection.unmask | text, mapping |
Маскирование выполняется в два шага. /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 Completions | 120 с |
| Генерация изображений | 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-ключ. |