API озвучення тексту
API озвучення тексту: увесь контракт — до реєстрації
У tonvio є публічний HTTP API для синтезу мовлення та клонування голосу. Ця сторінка — повний довідник: ендпоінти, поля запиту, ліміти, коди помилок і машинна специфікація OpenAPI 3.1. Нічого не сховано за логіном, тож інженер може оцінити інтеграцію раніше, ніж хтось заведе обліковий запис.
API працює на тому самому рушії, що й кабінет: ті самі голоси та можливості генерації, та сама черга, той самий готовий MP3.
Базова адреса
Усі шляхи нижче — відносно цієї адреси. API говорить лише на JSON; виняток один — multipart-ендпоінт для завантаження зразка голосу.
https://api.tonvio.net/api/v1Автентифікація
Передайте ключ як Bearer-токен у заголовку Authorization або в заголовку X-API-Key — це рівнозначно. Ключ — серверний секрет: хто ним володіє, той витрачає ваші кредити, тому у фронтенд його класти не можна. Ключі створюються в кабінеті й показуються повністю рівно один раз.
Authorization: Bearer tvk_your_key_here
# or
X-API-Key: tvk_your_key_hereЯк влаштована інтеграція
API асинхронний за задумом. Синтез довгого тексту триває хвилини, а HTTP-запит, що висить хвилинами, помирає десь посередині — у проксі, балансувальнику або CDN. Тому сервер приймає завдання, відповідає одразу, а клієнт опитує.
- 01
Створіть завдання
POST /speech з текстом та id голосу. Відповідь — 202 з id завдання і статусом queued. Кредити цієї миті резервуються, а не списуються.
- 02
Опитуйте статус
GET /speech/{id} кожні 2–3 секунди. progress сягає 100 лише разом зі статусом completed — доки завдання триває, стеля 99, тож «100%» ніколи не означає «майже готово».
- 03
Заберіть аудіо
За статусу completed в audio_url лежить посилання на MP3. Посилання короткоживуче (близько шести годин) — запитайте завдання ще раз і отримаєте свіже.
- 04
Обробіть термінальні стани
failed і canceled — теж кінець. За відмови резерв повертається, а error_code каже, чи є сенс повторювати. Повтор — це ЗАВЖДИ нове завдання і нове списання.
# 1. Create a job
curl -X POST https://api.tonvio.net/api/v1/speech \
-H "Authorization: Bearer tvk_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: replace-with-a-unique-request-id" \
-d '{ "text": "Hello from tonvio!", "voice_id": "el_XXXX", "language": "en" }'
# → 202 { "id": "JOB_ID", "status": "queued", "characters": 18, "queue_position": 1 }
# 2. Poll until completed (every 2-3 seconds)
curl https://api.tonvio.net/api/v1/speech/JOB_ID \
-H "Authorization: Bearer tvk_your_key_here"
# → { "status": "completed", "progress": 100, "audio_url": "https://cdn.tonvio.net/..." }
# 3. Or read what this key may actually spend and run into
curl https://api.tonvio.net/api/v1/account \
-H "X-API-Key: tvk_your_key_here"Ендпоінти
Чотирнадцять ендпоінтів і один конверт. Будь-який запит потребує ключа, а в кожного ключа є набір прав з останньої колонки.
| Метод | Шлях | Що робить | Право |
|---|---|---|---|
| POST | /speech | Створити завдання синтезу (асинхронно). | speech |
| GET | /speech/{id} | Статус завдання і, коли готово, посилання на аудіо. | speech:read |
| GET | /speech | Завдання цього ключа, свіжі першими, курсорна пагінація. | speech:read |
| POST | /speech/{id}/cancel | Зупинити завдання; за відрендерене списується, залишок повертається. | speech |
| DELETE | /speech/{id} | Видалити завдання та його аудіо; незавершене спершу зупиняє. | speech |
| GET | /voices | Каталог голосів, якими цей ключ може озвучувати. | voices |
| GET | /voices/{id} | Один голос за id. | voices |
| GET | /voices/{id}/warm | Чи готовий голос до синтезу. | voices |
| POST | /voices/{id}/warm | Прогріти голос до першого платного завдання. | voices |
| GET | /account | Баланс, чинні ліміти та витрати цього ключа. | account |
| POST | /voice-clones | Замовити клон голосу (асинхронно). | clone |
| GET | /voice-clones/{id} | Стан заявки і, коли готово, її voice_id. | clone |
| GET | /voice-clones | Заявки цього ключа, курсорна пагінація. | clone |
| DELETE | /voice-clones/{id} | Зняти заявку або видалити клон і звільнити слот у провайдера. | clone |
Права ключа
У кожного ключа свій набір прав. Заводський набір — speech, voices, account: він відкриває все, що API вмів і раніше. Право clone видається окремо. Звернення до ендпоінта без права — 403 insufficient_scope, а в полі required перелічено права, будь-якого з яких вистачило б. Ключу можна призначити й термін: після нього будь-який запит отримує 401 api_key_expired. Свої права й термін ключ читає в GET /account.
| Право | Що відкриває |
|---|---|
speech | Створювати й скасовувати завдання. Покриває і speech:read. |
speech:read | Лише читати стан завдань — право для спостерігача, якому не можна витрачати гроші. |
voices | Читати каталог голосів. |
account | Читати баланс, ліміти та витрати. |
clone | Замовляти й видаляти клони голосу. До заводського набору НЕ входить. |
POST /speech — тіло запиту
Ідентифікатор голосу потрібен один: або каталожний voice_id, або ідентифікатор того самого голосу в зовнішньому каталозі, на якому побудований перший провайдер.
| Поле | Значення | |
|---|---|---|
text | обов'язково | Текст для озвучення. Тариф обмежує символи на запит — чинна цифра в GET /account (limits.max_chars_per_request). |
voice_id | одне з двох | Каталожний id голосу з GET /voices або voice_id готового приватного клона. |
elevenlabs_voice_id | одне з двох | Ідентифікатор голосу в зовнішньому каталозі, на якому побудований перший провайдер. Провайдери, що з ним не працюють, його відхиляють. |
voice_engine | необов'язково | Рушій синтезу. У каталожного голосу береться його власний дефолт; у приватного клона дефолту немає — передайте явно. Провайдери без поняття «рушій» поле ігнорують. |
model | необов'язково | Псевдонім voice_engine. Якщо надіслані обидва, вони мають збігатися. |
language | необов'язково | Мова синтезу, BCP 47 (uk, en, pt-BR). Там, де голос — це стек клонів за мовами, мовчання не означає «хай вирішить рушій»: це мовчазний вибір. Мови, якими голос справді володіє, — у його полі languages. |
settings | необов'язково | Тонке налаштування: stability (0–1), settings_preset, delay_between_chunks (0–60 с). Незнайомий ключ відхиляється, а не ігнорується мовчки. |
idempotency_key | необов'язково | Те саме, що заголовок Idempotency-Key. Якщо надіслані обидва, вони мають збігатися. |
Що повертається
202 з id завдання, його статусом, зарезервованими символами та позицією в черзі. Якщо в тексті була розмітка, якої цільовий провайдер не розуміє, у відповіді з'явиться warnings: кожен замінений або знятий тег із лічильником. За знятий тег гроші не беруться — це єдине місце, де сервер змінює ваш текст, і він завжди про це каже.
Кредити, частота та обсягові квоти
API працює на куплених символьних кредитах: 1 символ = 1 кредит. Підписки й бонуси за нього не платять, а читальні запити баланс не витрачають узагалі. Скільки реально доступно — у GET /account (credits.api_spendable_chars); за нуля /speech відповідає 402 insufficient_balance.
- Частота: типово 300 запитів на хвилину на ключ. Ключу можна призначити свою цифру, а чинна приїздить у заголовках відповіді RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset і RateLimit-Policy. Ключ накопичує запас: короткий сплеск проходить, рівний перебір — 429 rate_limited.
- Паралельність: скільки завдань іде водночас, вирішують тариф і ліміти ключа. Типово це ТІ САМІ слоти, що й у кабінету, — вони спільні. Слотів не лишилося — 429 too_many_jobs, а поле scope каже, чия стеля спрацювала: account (спільна з кабінетом) чи key (власна стеля ключа).
- Обсяг: ключу можна призначити добовий і місячний кап символів. Обидва рахуються в UTC — добовий обнуляється о 00:00 UTC, місячний першого числа. Перевищення — 429 quota_exceeded з полями period, used, limit і resets_at; залишок лежить в usage.key.
- Усі чинні цифри GET /account віддає разом із limit_sources — картою, де сказано, звідки кожна взялася: key (призначена на ключі), plan (успадкована від тарифу власника) або default (службове значення сервісу, якого немає ні там, ні там).
Ідемпотентність
Передайте idempotency_key у тілі або заголовок Idempotency-Key — і повтор стане безпечним: той самий ключ з тими самими параметрами поверне вихідне завдання, а не створить (і не оплатить) друге. Той самий ключ з іншими параметрами — 409 idempotency_mismatch; ключ, чий результат уже видалено, — 409 idempotency_key_consumed. Без ключа кожен виклик створює нове завдання. У замовленні клона заголовок обов'язковий, і причина груба: повтор замовлення коштує слота в провайдера, а ретраєм слот не повертається.
Клонування голосу через API
Клон замовляється асинхронно, як і озвучення: POST /voice-clones відповідає 202 з нашим id заявки, а клієнт опитує GET /voice-clones/{id} не частіше, ніж сказано в poll_after_seconds. Складання голосу триває хвилини, а не секунди. Щойно status став completed, voice_id — це те, що ви передасте в POST /speech.
- Два режими. Приватний клон складається з вашого зразка (multipart: mp3, wav, m4a або webm, до 15 МБ; формат перевіряється за РЕАЛЬНИМИ байтами, а не за іменем файлу). Мовний варіант каталожного голосу замовляється в JSON — посилання на файл ми свідомо не приймаємо.
- Мовний варіант — СПІЛЬНИЙ: складений голос побачать усі користувачі сервісу, видалити його не можна, тому замовлення вимагає явного acknowledge_shared: true. Що ви отримали насправді, завжди сказано в полі visibility — private або shared.
- consent обов'язковий в обох формах: заява про право на цей голос завдовжки 20–500 символів. Вона зберігається разом із заявкою — поза кабінетом це єдиний слід того, хто заявив право. Клонуйте лише свій голос або голос, на який маєте письмовий дозвіл.
- Не кожен провайдер уміє кожен вид клона. Там, де поняття немає, відповідь — 501 clone_not_supported_for_provider, причому ДО завантаження файлу: нічого не надіслано і нічого не зарезервовано.
- Приватного клона немає в публічному каталозі, і рушія за замовчуванням у нього теж немає: під час синтезу ним передавайте voice_engine явно.
- DELETE /voice-clones/{id} звільняє слот у провайдера й ідемпотентний (204, без тіла). Доки провайдер тримає заявку, прийде 409 clone_in_progress; спільний мовний варіант не видаляється зовсім.
# Order a private clone from your own sample.
# The text fields MUST come before the file, or they never reach the server.
curl -X POST https://api.tonvio.net/api/v1/voice-clones \
-H "Authorization: Bearer tvk_your_key_here" \
-H "Idempotency-Key: another-unique-request-id" \
-F "name=Narrator" \
-F "consent=I confirm this is my own voice and I may clone it." \
-F "languages=en,de" \
-F "[email protected]"
# → 202 { "id": "CLONE_ID", "status": "queued", "visibility": "private",
# "percent": 0, "poll_after_seconds": 15 }
# Poll until completed, then synthesize with the resulting voice_id.
# A private clone has no default engine, so pass voice_engine explicitly.
curl -X POST https://api.tonvio.net/api/v1/speech \
-H "Authorization: Bearer tvk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "text": "Hello!", "voice_id": "VOICE_ID", "voice_engine": "ENGINE_ID" }'Помилки
У будь-якої відмови один конверт: машинний код error плюс поля, які несе саме цей код, і чесний HTTP-статус. Розгалужуйтеся за кодом, а не за формулюванням message. Заголовок відповіді X-Request-Id зберігайте для підтримки. Повний перелік кодів по кожному ендпоінту — у специфікації OpenAPI; нижче ті, що трапляються найчастіше.
| Код | Що означає |
|---|---|
invalid_request | Тіло запиту невалідне — дивіться message. |
missing_api_key | Ключ не передано. |
invalid_api_key | Ключ невідомий або відкликаний. |
api_key_expired | У ключа минув термін. |
api_key_frozen | З ключа знято всі права. Оборотно — зверніться до власника сервісу. |
insufficient_scope | У ключа немає права на цей ендпоінт; у required перелічено, чого вистачило б. |
insufficient_balance | Бракує куплених символів — need каже, скільки коштує запит. |
rate_limited | Надто часто для цього ключа. Зачекайте retry_after секунд. |
too_many_jobs | Вільних слотів немає; scope каже, чия стеля спрацювала — account чи key. |
quota_exceeded | Добовий або місячний обсяг ключа вичерпано — див. period, used, limit і resets_at. |
voice_not_allowed | Цим ключем такий голос використовувати не можна. |
voice_warming | Голос прогрівається — повторіть трохи згодом. |
voice_language_not_available | Голос не говорить запитаною мовою; в available перелічено ті, якими говорить. |
voice_engine_required | У голосу немає рушія за замовчуванням — передайте voice_engine явно. |
text_too_long | Текст довший за ліміт тарифу на один запит; у max сам ліміт. |
payload_too_large | Тіло запиту більше за межу; у max_bytes сама межа. |
idempotency_mismatch | Цей ключ ідемпотентності вже використано з іншими параметрами. |
generation_unavailable | Генерація тимчасово недоступна — повторіть після зазначеної затримки. |
internal_error | Несподівана помилка сервера. Повторіть пізніше й передайте підтримці request_id. |
Є й друга родина відмов — саме та, з якою інтеграція стикається найчастіше: запит ПРИЙНЯТО (202), а не склалося вже потім. Такі приїздять у error_code в GET /speech/{id} зі статусом failed: queue_timeout, execution_timeout, provider_unavailable, voice_unavailable, synthesis_failed, assembly_failed, output_too_large і generation_failed на все інше. Зарезервовані кредити повертаються в кожному з цих випадків, а error_message — англійська фраза, яку можна показати людині.
Машинна специфікація
Увесь контракт опубліковано одним документом OpenAPI 3.1: кожен ендпоінт, параметр, форма відповіді, код помилки та обидві схеми автентифікації. Довідник вище написано з того самого джерела.
- Згенеруйте клієнт своєю мовою замість ручних HTTP-викликів.
- Імпортуйте в HTTP-клієнт — усі запити будуть заповнені наперед.
- Віддайте нейромережі: це самодостатній опис сервісу.
Перевіряється за мета-схемою OpenAPI 3.1 за кожної зміни.
Специфікація — звичайний JSON: віддайте її генератору клієнта, HTTP-клієнту або нейромережі — і все запрацює.
Що інженери запитують найпершим
Документацію можна читати без облікового запису?
Так — ця сторінка й специфікація OpenAPI відкриті. Обліковий запис потрібен лише щоб завести ключ і витрачати кредити, тож оцінити інтеграцію, згенерувати клієнт і розібрати контракт помилок можна раніше, ніж хтось щось підпише.
API синхронний?
Синтез довгого тексту триває хвилини, а запит, що висить стільки ж, помирає у проксі або CDN, тому POST /speech одразу повертає id завдання. Дізнатися про результат можна трьома способами, від простого до зручного: опитувати GET /speech/{id} не частіше, ніж каже його poll_after_seconds; додати ?wait=60 і тримати запит відкритим до кінця роботи (long polling); або задати ключу адресу вебхука й отримувати події speech.completed і speech.failed. Вебхуки підписані HMAC-SHA256 і повторюються при збої, але це сигнал, а не сховище: посилання в тілі живе недовго, по свіже йдіть до GET /speech/{id}.
Скільки коштує виклик API?
Символи. Один символ тексту — один кредит, і беруться вони лише з куплених пакетів: підписки й бонуси за API не платять. Читальні запити безкоштовні. Кредити резервуються під час приймання завдання і повертаються за відмови чи скасування; ціни — на сторінці тарифів.
Які ліміти за частотою?
Типово 300 запитів на хвилину на ключ, із запасом, що гасить короткі сплески; ключу можна призначити свою цифру. Скільки завдань іде водночас, вирішують тариф і ліміти ключа, а ще в ключа можуть бути добовий і місячний капи символів. Чинні значення — у заголовках RateLimit-* і в GET /account.
Чи можна обмежити окремий ключ?
Так. У ключа є набір прав — наприклад, лише читання статусів для моніторингу, — і йому можна призначити свою частоту, слоти, капи символів, дозволених провайдерів, квоту клонів і термін дії. Ключ, з якого знято всі права, заморожений і відповідає 403, доки права не повернуть.
Як зробити повтори безпечними?
Надсилайте Idempotency-Key. Той самий ключ з тими самими параметрами поверне вихідне завдання, а не створить друге, — і мережевий таймаут не перетвориться на подвійне списання. У замовленні клона заголовок обов'язковий: повтор замовлення коштує слота в провайдера.
Голос можна клонувати через API?
Так, якщо в ключа є право clone. Завантажте зразок, опитуйте заявку до статусу completed і користуйтеся отриманим voice_id як будь-яким іншим голосом. Клонуйте лише свій голос або той, на який маєте письмовий дозвіл: кожне замовлення зберігає вашу заяву про право.
Скільки живе посилання на аудіо?
Близько шести годин. Забирайте файл, коли завдання завершилося; якщо посилання знадобиться пізніше — запитайте завдання ще раз, і видасться свіже. Готове аудіо зберігається і в історії кабінету.
Почніть з одного запиту
Заведіть ключ у кабінеті — перший виклик забере хвилину. Перше озвучення безкоштовне, тож увесь цикл (створити, опитати, завантажити) можна перевірити, нічого не витративши.
Уперше в нас? Почніть з огляду озвучення тексту нейромережею.