HiAPI
Блог

Руководство

Как вызывать gemini-3.1-flash-tts на HiAPI: curl, Python и рабочий запрос

Oct 31, 2026
深靛蓝色背景上多层嵌套的宽箭头依次向右上方推进,箭头前缘为杏橙色,构成规整的信号流示意图形,这是文章的封面图。

Что gemini-3.1-flash-tts может сделать для вас на HiAPI

gemini-3.1-flash-tts — это модель преобразования текста в речь, доступная через единый API HiAPI. Вы отправляете текст и конфигурацию голоса, а платформа возвращает сгенерированное аудио. Поскольку модель доступна через единый, согласованный интерфейс, вам не нужно интегрировать каждую модель отдельно — одинаковые заголовки аутентификации, одинаковый базовый URL и одинаковый шаблон асинхронных задач применимы ко всем TTS-моделям на платформе.

Выбирайте эту модель, когда вам нужны:

  • Многоголосое озвучивание. Сценарии, диалоги или контент в стиле подкаста, где разные фрагменты должны звучать как разные голоса.
  • Быстрая обработка. Серия flash ориентирована на генерацию с низкой задержкой, что особенно важно для интерактивных приложений или пакетных задач с жёсткими SLA.
  • Предсказуемый интерфейс интеграции. Одна форма запроса покрывает всё — от одной строки текста до сценариев с несколькими говорящими.

Если ваше приложение уже вызывает другие модели HiAPI, добавление голосовых функций сводится к замене идентификатора модели и настройке тела запроса — не нужен новый SDK и не нужен новый процесс получения учётных данных.

Предварительные условия и настройка аутентификации

Перед первым вызовом убедитесь, что у вас есть:

  1. Учётная запись HiAPI и API-ключ. Сгенерируйте ключ в консоли. Относитесь к нему как к секрету — не коммитьте его в систему контроля версий.
  2. Базовый URL HiAPI. Все запросы отправляются на один и тот же хост; различаются только пути каждого эндпоинта.
  3. Инструмент для выполнения HTTP-запросов. curl для быстрого тестирования, а также Python 3.9+ и библиотека requests для скриптов.

Аутентификация выполняется путём передачи bearer-токена в заголовке Authorization:

Authorization: Bearer $HIAPI_API_KEY

Храните ключ в переменной окружения, а не в исходном коде:

export HIAPI_API_KEY="your_key_here"

Каждый описанный ниже эндпоинт — отправка задачи, проверка статуса и получение файла — использует один и тот же заголовок. Если вы получили 401, значит заголовок отсутствует, имеет неверный формат или ключ был отозван. Обновите ключ в консоли, а не используйте один и тот же ключ в разных средах.

Формирование тела запроса для многоголосовой речи

Тело запроса — это JSON. Как минимум оно должно содержать идентификатор модели и входные данные. Для многоголосового вывода вам нужно добавить конфигурацию говорящих, сопоставляющую именованных говорящих с голосами, и разметить входной текст тегами говорящих.

Типичное тело запроса выглядит так:

{
  "model": "gemini-3.1-flash-tts",
  "input": {
    "text": "[Host] Welcome back to the show. [Guest] Thanks for having me.",
    "multi_speaker": true,
    "speakers": [
      { "name": "Host", "voice": "voice_a" },
      { "name": "Guest", "voice": "voice_b" }
    ]
  }
}

Ключевые моменты, на которых легко споткнуться:

  • Имена говорящих должны совпадать точно. Поле name в массиве speakers должно в точности совпадать с тегом, используемым в тексте (в данном примере — в квадратных скобках). Несовпадение регистра — самая частая причина сбоя многоголосового рендеринга.
  • Каждый тег должен быть объявлен. Если в тексте упоминается [Narrator], но говорящий с name: "Narrator" не определён, запрос будет отклонён.
  • Идентификаторы голосов берутся из каталога голосов платформы. Используйте идентификаторы, предоставляемые HiAPI, а не придумывайте свои. Для задач с одним говорящим опустите multi_speaker и speakers и вместо этого укажите одно поле voice.
  • Текст отправляется одной строкой. Длинные сценарии не проблема, но учитывайте ограничения на запрос, задокументированные на платформе.

После проверки тела запроса задача принимается и возвращается идентификатор задачи, который вы будете использовать для опроса.

Запуск с помощью curl

Сохраните приведённое выше тело запроса в payload.json, затем отправьте:

curl -X POST "$HIAPI_BASE_URL/v1/audio/speech/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json

Ответ содержит объект задачи с полями id и status (обычно queued или processing):

{ "id": "job_abc123", "status": "queued" }

Чтобы проверить прогресс, получите задачу по ID:

curl "$HIAPI_BASE_URL/v1/audio/speech/jobs/job_abc123" \
  -H "Authorization: Bearer $HIAPI_API_KEY"

Когда status становится succeeded, ответ содержит ссылку на аудиофайл (URL или ID файла), который можно скачать последующим запросом — см. раздел об опросе ниже. Если задача не удалась, status будет failed, а поле error объяснит причину.

Для быстрых экспериментов можно пропустить файл и передать JSON напрямую:

curl -X POST "$HIAPI_BASE_URL/v1/audio/speech/jobs" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gemini-3.1-flash-tts","input":{"text":"Hello from HiAPI.","voice":"voice_a"}}'

Запуск с помощью Python

Версия на Python использует requests и небольшую вспомогательную функцию, чтобы цикл опроса оставался читаемым. Тело запроса идентично примеру с curl.

import os
import time
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

payload = {
    "model": "gemini-3.1-flash-tts",
    "input": {
        "text": "[Host] Welcome back to the show. [Guest] Thanks for having me.",
        "multi_speaker": True,
        "speakers": [
            {"name": "Host", "voice": "voice_a"},
            {"name": "Guest", "voice": "voice_b"},
        ],
    },
}

resp = requests.post(
    f"{BASE_URL}/v1/audio/speech/jobs",
    headers={**HEADERS, "Content-Type": "application/json"},
    json=payload,
    timeout=30,
)
resp.raise_for_status()
job = resp.json()
job_id = job["id"]
print("submitted", job_id)

while True:
    r = requests.get(f"{BASE_URL}/v1/audio/speech/jobs/{job_id}", headers=HEADERS, timeout=30)
    r.raise_for_status()
    status = r.json()
    if status["status"] in ("succeeded", "failed"):
        break
    time.sleep(2)

if status["status"] == "failed":
    raise RuntimeError(status.get("error"))

print("audio ready:", status["output"])

Замечания:

  • Тайм-ауты важны. Явно задавайте тайм-аут для каждого запроса; зависший цикл опроса хуже, чем цикл, который завершился с ошибкой.
  • Не опрашивайте слишком часто. Для коротких сценариев разумна двухсекундная пауза; для длинных сценариев следует увеличивать интервал.
  • Поместите тело запроса в функцию. В реальном коде параметризуйте текст, говорящих и голоса, чтобы повторно использовать одну и ту же логику отправки для задач с одним говорящим.

Опрос асинхронных задач и скачивание аудио

Генерация речи асинхронна: вы отправляете задачу, затем проверяете статус, пока задача не достигнет терминального состояния. Существует два терминальных состояния — succeeded и failed. Все остальные состояния следует рассматривать как «продолжайте ждать».

Надёжная программа опроса должна:

  1. Ограничивать общее время ожидания. Даже быстрая модель может оказаться в очереди после другой работы. Остановитесь после разумного предела и верните ID задачи, чтобы вызывающая сторона могла повторить попытку.
  2. Использовать экспоненциальную задержку. Начните с одной-двух секунд и удваивайте до верхнего предела. Это позволяет избежать чрезмерной нагрузки на API при длительных задачах.
  3. Явно обрабатывать ошибки. Когда status == "failed", прочитайте поле error и зафиксируйте его — оно обычно указывает на проблему в теле запроса (необъявленный говорящий, пустой текст, неподдерживаемый голос).

При успешном выполнении задачи ответ со статусом содержит ссылку на вывод. Скачайте его с тем же заголовком аутентификации:

out = status["output"]
audio = requests.get(out["url"], headers=HEADERS, timeout=60)
audio.raise_for_status()
with open("narration.wav", "wb") as f:
    f.write(audio.content)

Если в ответе возвращается ID файла, а не URL, получите его через соответствующий эндпоинт файлов на HiAPI. Сохраняйте исходный ID задачи вместе с сохранённым файлом — это единственный надёжный способ позже проследить неудачный рендеринг до его запроса.

Понимание оплаты по символам

На HiAPI gemini-3.1-flash-tts тарифицируется по количеству символов входного текста. Нет компонентов оплаты за запрос или за минуту, которые нужно моделировать — стоимость задачи растёт вместе с объёмом отправляемого текста, что делает оценку простой: общее количество символов во всех сценариях, умноженное на текущую ставку за символ.

Практические следствия:

  • Оценивайте до запуска. Суммируйте ожидаемый месячный объём текста и сверьте его с текущей ставкой, указанной на панели тарифов. Не встраивайте ставку в код; считывайте её с платформы во время сборки или храните в конфигурации.
  • Длинный контент определяет стоимость. Десятиминутное озвучивание — это в основном стоимость текста. Если вы создаёте аудиокниги или учебные материалы, сначала смоделируйте объём.
  • Повторные попытки стоят денег. Задачи, завершившиеся с ошибкой после генерации, в зависимости от характера ошибки всё равно могут тарифицироваться. Выполняйте клиентскую валидацию перед отправкой — проверяйте, что каждый тег говорящего объявлен, а текст не пуст.
  • Активно кэшируйте. Если один и тот же сценарий рендерится повторно (например, для демонстрации или тестового стенда), сохраняйте сгенерированное аудио и переиспользуйте его, а не платите повторно за одни и те же символы.

Актуальные ставки и любые уровни объёма см. на панели тарифов на сайте — это единственный источник истины.

Типичные точки отказа и способы их устранения

Большинство проблем интеграции сводятся к нескольким категориям. Проверяйте их по порядку.

401 Unauthorized

Ваш заголовок Authorization отсутствует или имеет неверный формат. Убедитесь, что ключ задан в окружении, а содержимое заголовка в точности равно Bearer <key>. Если ключ был обновлён, обновите и окружение.

400 Bad Request — неизвестный говорящий

Текст содержит тег [Name], не объявленный в speakers, или регистр не совпадает. Сделайте тег и поле name побайтово идентичными.

400 Bad Request — пустой или слишком большой ввод

Пустой текст не проходит валидацию сразу. Очень длинные сценарии могут превышать лимит на запрос; разбейте их на несколько задач, а не обрезайте.

400 Bad Request — недопустимый идентификатор голоса

Имена голосов должны браться из каталога платформы. Копируйте идентификаторы точно; не придумывайте собственные удобные названия.

Задача застревает в состоянии processing

Обычно это очередь, а не баг. Применяйте задержку и общий тайм-аут. Если это продолжается значительно дольше обычного, запишите ID задачи и один раз повторите с меньшим телом запроса, чтобы понять, не в размере ли проблема.

Задача failed с ошибкой генерации

Прочитайте поле error. Оно обычно указывает на проблему с контентом или конфигурацией, а не на временный сбой. Исправьте тело запроса, отправьте повторно и избегайте слепых циклов повторных попыток — каждая попытка может тарифицироваться по символам.

Аудио скачивается, но не воспроизводится

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

С этими проверками большинство проблем интеграции первого дня решаются за минуты, а не за часы.

Похожие статьи

Все статьи →

Начните работать с постоянными артефактами ИИ

Получите API-ключ и вызывайте модели изображений, видео и аудио через один API на основе tasks.

Читать документацию API