Hubris
Скачать из Google PlayСкачайте из RuStore
hubris

POST /v1/audio/speech

Озвучка текста (TTS) — текст на входе, готовый аудиофайл на выходе. OpenAI-совместимый формат, работает официальный openai SDK.

Превращает текст в речь: озвучка статей и рассылок, голосовые ответы бота, аудиоверсии инструкций, реплики персонажей. OpenAI-совместимый формат — без переписывания работают openai Python/TypeScript SDK и любые клиенты под OpenAI Audio API: достаточно поменять base_url.

Ответ — сами байты аудио, а не JSON: сохраняйте их в файл или отдавайте плееру. Стрима по флагу нет — один POST возвращает один готовый ответ целиком (его можно читать потоково, но это всё тот же единственный ответ). Модели озвучки — в каталоге (фильтр «Озвучка») или запросом GET /v1/models?output_modalities=speech: MAI Voice, Gemini TTS, Aura-2, MiniMax Speech, Kokoro, Orpheus, Voxtral TTS и другие.

Эндпоинт

POSThttps://api.hubris2.ru/v1/audio/speech

Заголовки:

HeaderЗначение
AuthorizationBearer sk-gw-... (обязательно)
Content-Typeapplication/json

Тело запроса

ПолеТипПо умолчаниюОписание
modelstring—ID модели озвучки из каталога.
inputstring—Текст для озвучки, до 50 000 символов. Как считается цена — в разделе «Биллинг».
voicestring—Голос модели. У каждой модели свой набор — см. ниже.
response_formatstringзависит от моделиmp3 или pcm (16-bit LE). Если поле не передать, формат выбирает модель — как правило, это pcm; для файла на диске указывайте mp3 явно. Gemini TTS сама отдаёт только pcm, mp3 для неё собираем мы.

Дополнительные поля, которые понимает конкретная модель (например, скорость речи или инструкции по стилю), передаются ей как есть — схема их не отбрасывает. Тело запроса ограничено 1 МБ: это текст, а не медиа, лимита хватает с большим запасом.

Голоса

Наборы голосов у моделей не пересекаются: где-то их два, где-то девяносто. Список голосов конкретной модели — в её карточке в каталоге, и если он есть, voice обязателен: без него запрос отклоняется с кодом 400, code: "voice_required". Часть провайдеров список не публикует — тогда модель звучит голосом по умолчанию, а другой голос берите из документации самой модели.

Если голос не из списка, который знает каталог, запрос отклоняется до обращения к модели — с кодом 400, code: "voice_not_supported", и в тексте ошибки перечислены доступные значения:

{
  "error": {
    "message": "Голос «нет-такого» недоступен у модели microsoft/mai-voice-2-flash. Доступные: en-US-Harper:MAI-Voice-2, …",
    "type": "invalid_request_error",
    "code": "voice_not_supported"
  }
}

Если голосов у модели много, в сообщении перечислены первые восемь и число оставшихся. За отклонённый таким образом запрос ничего не списывается.

Минимальный пример

Модель и голос всегда идут парой: сменили модель — берите голос из её карточки в каталоге.

curl -s https://api.hubris2.ru/v1/audio/speech \-H "Authorization: Bearer sk-gw-..." \-H "Content-Type: application/json" \-d '{  "model": "microsoft/mai-voice-2-flash",  "voice": "en-US-Harper:MAI-Voice-2",  "input": "Привет! Это проверка озвучки.",  "response_format": "mp3"}' \--output speech.mp3
from openai import OpenAIclient = OpenAI(  base_url="https://api.hubris2.ru/v1",  api_key="sk-gw-...",)# Модель и голос — из карточки модели в каталоге /modelsMODEL = "microsoft/mai-voice-2-flash"VOICE = "en-US-Harper:MAI-Voice-2"with client.audio.speech.with_streaming_response.create(  model=MODEL,  voice=VOICE,  input="Привет! Это проверка озвучки.",  response_format="mp3",) as response:  response.stream_to_file("speech.mp3")
import fs from "node:fs";import OpenAI from "openai";const client = new OpenAI({baseURL: "https://api.hubris2.ru/v1",apiKey: "sk-gw-...",});// Модель и голос — из карточки модели в каталоге /modelsconst MODEL = "microsoft/mai-voice-2-flash";const VOICE = "en-US-Harper:MAI-Voice-2";const speech = await client.audio.speech.create({model: MODEL,voice: VOICE,input: "Привет! Это проверка озвучки.",response_format: "mp3",});fs.writeFileSync("speech.mp3", Buffer.from(await speech.arrayBuffer()));

Python-вариант с with_streaming_response читает тот же единственный ответ по мере поступления байтов — это удобно для длинного текста, но никакого пофразового стрима, как в чате, здесь нет.

Ответ

200 — тело целиком состоит из байтов аудио. JSON в успешном ответе не приходит, поэтому и поля usage в нём нет: расход по запросу смотрите в журнале.

ЗаголовокЗначение
Content-Typeaudio/mpeg при response_format: "mp3", audio/pcm при pcm — с частотой и числом каналов, например audio/pcm;rate=24000;channels=1.
Content-LengthРазмер аудио в байтах.
Cache-Controlno-store — ответ не кэшируется.

pcm — это сырые сэмплы без заголовка WAV: их либо скармливают плееру/микшеру напрямую, либо оборачивают в контейнер. Если нужен файл, который просто откроется двойным кликом, берите mp3.

Ошибки, наоборот, всегда приходят JSON-ом в обычном формате — по Content-Type ответа легко отличить одно от другого.

HTTP-коды

КодcodeКогда
200—Успех, в теле — аудио.
400voice_requiredУ модели есть список голосов, а voice не передан (в тексте ошибки — доступные).
400voice_not_supportedГолос не входит в набор этой модели (в тексте ошибки — доступные).
400invalid_requestТело не соответствует схеме: нет model или input, неизвестный response_format, невалидный JSON.
401—Ключ отсутствует / отозван / невалиден.
402insufficient_balanceНедостаточно средств на балансе.
404model_not_foundМодели с таким model не существует или это не модель озвучки.
413request_too_largeТело запроса больше 1 МБ.
429—Превышен лимит расхода ключа.
502pricing_unavailableУ модели нет ставки в каталоге или провайдер не сообщил, как измерить звук, — считать стоимость нечем. Списания нет.
502no_audio_returnedМодель ответила успехом, но пустым телом. Списания нет, запрос стоит повторить.
502 / 503 / 504—Транзиентные сбои провайдера, курса ЦБ или таймаут озвучки.

Формат тела ошибки и стратегия retry — Ошибки.

Биллинг

Цена зависит от того, в какой единице модель тарифицирует провайдер, — она указана в карточке модели в каталоге:

  • За символы текста — большинство моделей (MAI Voice, ElevenLabs, MiniMax и другие). Стоимость известна заранее: сколько символов отправили, столько и оплатили, независимо от того, насколько неторопливо модель их проговорила.
  • За байты текста — Fish Audio: текст считается в байтах UTF-8, буква кириллицы — два байта. Стоимость тоже известна заранее.
  • За секунды звука — Seed Audio: оплачивается длительность получившегося звука.
  • За токены — Gemini TTS. Текст на входе считается токенами, звук на выходе — токенами звука: 25 токенов на секунду звучания, то есть 1 500 за минуту. Цена зависит от длины получившегося звука; в журнале видно, сколько токенов ушло на вход и на выход. Слишком длинный текст Gemini обрывает без ошибки: gemini-3.1-flash-tts-preview целиком читает около 3 000 символов за запрос. Списывается только получившийся звук, длинный текст отправляйте частями.

Списание — по факту выполненного запроса, в рублях; сумма попадает в журнал запросов вместе с моделью и временем. Перед запросом проверяем, что денег на балансе хватает на оценку его цены. За отклонённые до обращения к модели запросы (voice_required, voice_not_supported, model_not_found, невалидное тело) и за пустой ответ модели не списывается ничего.

Что дальше

  • POST /v1/audio/transcriptions — обратная задача: запись в текст.
  • Аудио в чате — модель рассуждает о записи или отвечает голосом внутри диалога.
  • Модели — как выбрать модель и прочитать её карточку.
  • Ошибки — что делать на 429/502.
  • Цены — как считается стоимость запроса.

Обновлено: