API озвучення тексту
Tonvio API: жива мова для вашого продукту
Один HTTP API, щоб перетворювати текст на мовлення: понад 18 000 голосів, теги емоцій, клонування голосу та вебхуки. Увесь довідник відкритий — читайте тут, завантажте його в Markdown або OpenAPI й оцініть інтеграцію ще до реєстрації.
API працює на тому самому рушії, що й кабінет: ті самі голоси та можливості генерації, та сама черга, той самий готовий MP3.
Візьміть увесь довідник із собою: файл Markdown для команди чи AI-асистента або специфікацію OpenAPI для генераторів коду та HTTP-клієнтів.
Перетворюйте текст на природне мовлення через HTTP. Ви надсилаєте текст і голос — Tonvio повертає MP3. Завдання виконуються асинхронно: створіть завдання, дочекайтеся, поки воно завершиться, і завантажте аудіо. Усе, що описано нижче, працює з будь-яким HTTP-клієнтом. Увесь довідник також доступний як файл OpenAPI і як один файл Markdown.
Базовий URL: https://api.tonvio.net/api/v1
Швидкий старт
- 1Створіть ключ API у вебзастосунку: Сервіс API → Ключі API. Ключ показується лише один раз — збережіть його на своєму сервері.
- 2Виберіть голос через
GET /voicesі скопіюйте йогоid. - 3Створіть завдання через
POST /speech: передайте текст,voice_idі, за бажанням,language. Ви одразу отримаєтеidзавдання. - 4Викликайте
GET /speech/{id}?wait=60, докиstatusне станеcompleted, а тоді завантажте файл за посиланнямaudio_url.
# 1. Pick a voice
curl "https://api.tonvio.net/api/v1/voices?language=en&limit=3" \
-H "Authorization: Bearer tvk_your_key_here"
# 2. Create a job (save the "id" from the answer)
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: 7d0c3c1e-5b8a-4f61-9a43-2f1c9e0b6d11" \
-d '{ "text": "Hello! This is my first voiceover.", "voice_id": "voc_2072b6f1a9c4", "language": "en" }'
# → 202 { "id": "JOB_ID", "status": "queued", "characters": 34, "poll_after_seconds": 10 }
# 3. Wait for it: the server holds this request until the job is done (up to 60 s).
# Repeat while "status" is "queued" or "processing".
curl "https://api.tonvio.net/api/v1/speech/JOB_ID?wait=60" \
-H "Authorization: Bearer tvk_your_key_here"
# → { "status": "completed", "progress": 100, "audio_url": "https://cdn.tonvio.net/…" }
# 4. Download the MP3 (the link is valid for about 6 hours)
curl -L -o voiceover.mp3 "AUDIO_URL"Надсилайте заголовок Idempotency-Key з кожним POST /speech і передавайте те саме значення, коли повторюєте запит. Тоді втрачена відповідь чи збій мережі не створить другого завдання — і ви не заплатите за нього.
Автентифікація
Передавайте ключ у заголовку Authorization: Bearer або в X-API-Key — обидва способи працюють однаково. Ключ — це пароль до вашого балансу: зберігайте його на своєму сервері й ніколи не вбудовуйте в сайт чи мобільний застосунок.
Authorization: Bearer tvk_your_key_here
# or
X-API-Key: tvk_your_key_hereКожен ключ має права. Новий ключ отримує speech, voices і account. Для клонування голосу потрібне право clone — його надають окремо. Запит без потрібного права повертає 403 insufficient_scope.
| Право | Що дозволяє |
|---|---|
speech | Створювати, скасовувати й видаляти завдання (включає speech:read). |
speech:read | Лише читати завдання — для інструментів моніторингу, які не мають витрачати гроші. |
voices | Переглядати голоси. |
account | Читати баланс, ліміти й використання; керувати доставками вебхуків. |
clone | Створювати й видаляти клони голосу. |
Голоси
GET /voices повертає всі голоси, якими ви можете користуватися, — їх понад 18 000. Фільтруйте за language (наприклад, en, pt-BR), gender і q (пошук за назвою та описом). Гортайте сторінки за допомогою limit (до 200, за замовчуванням 50) і offset. У відповіді є поле total.
curl "https://api.tonvio.net/api/v1/voices?language=en&gender=female&limit=2" \
-H "Authorization: Bearer tvk_your_key_here"
{
"total": 4120,
"limit": 2,
"offset": 0,
"voices": [
{
"id": "voc_2072b6f1a9c4",
"name": "Marcus",
"language": "en",
"languages": ["en", "de"],
"languages_on_demand": ["fr", "es"],
"gender": "male",
"age": "middle_aged",
"accent": "american",
"description": "Calm, warm narrator",
"preview_url": "https://cdn.tonvio.net/voice-previews/…/sample.mp3",
"origin": "vendor"
},
…
]
}| Поле | Значення |
|---|---|
id | Те, що ви передаєте як voice_id у POST /speech. Використовуйте його точно в тому вигляді, у якому воно прийшло. |
name, description, gender, age, accent | Що це за голос: ім’я, опис, стать, вік, акцент. Будь-яке з цих полів може бути null. |
preview_url | Короткий зразок для прослуховування. |
language | Рідна мова голосу. |
languages | Мови, якими голос говорить одразу. |
languages_on_demand | Мови, якими голос теж може говорити. Перше завдання такою мовою чекає кілька хвилин, поки голос готують (стадія awaiting_voice); після цього завдання стартують одразу. |
origin | vendor — готовий голос із каталогу; recording — голос, клонований із запису. |
GET /voices/{id} повертає один голос. Деякі інші поля у відповіді залишені для старих інтеграцій — їх можна ігнорувати.
Створення мовлення
POST /speech приймає завдання й одразу відповідає 202 Accepted. У відповіді є id завдання та заголовок Location, що вказує на нього.
| Поле | Значення | |
|---|---|---|
text | обов’язково | Що озвучити. До limits.max_chars_per_request символів — дивіться GET /account (залежить від вашого тарифу). Має містити хоча б одну літеру чи цифру. |
voice_id | обов’язково | id голосу з GET /voices або voice_id вашого власного клону. |
language | необов’язково | Мова тексту у форматі коду BCP 47: en, de, pt-BR. Якщо її не вказати, мову буде визначено за текстом. |
settings.voice.speed | необов’язково | Темп мовлення від 0.5 до 2, за замовчуванням 1. Висота голосу не змінюється. |
settings.voice.pauses | необов’язково | Паузи в мілісекундах (0–5000) після sentence (речення), paragraph (абзацу), clause (частини речення) або word (слова). Задавайте будь-які з них. Абзаци відокремлюються порожнім рядком. |
idempotency_key | необов’язково | Те саме, що заголовок Idempotency-Key (до 128 символів). Використовуйте щось одне. |
{
"text": "Welcome back. [pause] Today we have big news! [excited] Let's begin.",
"voice_id": "voc_2072b6f1a9c4",
"language": "en",
"settings": {
"voice": {
"speed": 1.1,
"pauses": { "sentence": 350, "paragraph": 800 }
}
}
}Емоції та звуки
Додавайте короткі вказівки англійською у квадратних дужках прямо в текст: [whisper], [laughing], [sigh], [pause], [excited], [clearing throat], [emphasis], [shouting]. Манеру можна описати й своїми словами, до 64 символів: [whisper in small voice]. Дужки, що починаються з цифри або написані не латиницею, — [1], [глава 2] — озвучуються як звичайний текст. Теги — частина тексту, тож вони теж рахуються як символи.
Відповідь
HTTP/1.1 202 Accepted
Location: /api/v1/speech/0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12
{
"id": "0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12",
"status": "queued",
"characters": 68,
"paid_with": "credits",
"queue_position": 0,
"poll_after_seconds": 10
}characters— скільки символів зарезервовано під це завдання.queue_position— скільки ваших завдань чекає в черзі перед цим.poll_after_seconds— коли вперше запитати про стан завдання.settings_warnings— з’являється, лише якщо щось із надісланого не було застосовано, і містить назву поля та причину. Наприклад, швидкість поза межами 0.5–2.
Старі інтеграції можуть і далі надсилати voice_engine, model, settings.stability, settings.settings_preset, settings.delay_between_chunks, settings.voice.chunking або settings.voice.delivery. Ці поля приймаються для сумісності, не впливають на звук і перелічуються в settings_warnings.
Стан і прогрес завдання
GET /speech/{id} повертає поточний стан завдання. Завдання проходить шлях queued → processing → completed. Воно також може завершитися станом failed або canceled. Ці три стани остаточні.
| Поле | Значення |
|---|---|
status | queued — чекає на вільний слот; processing — озвучується; completed, failed, canceled — завершено. |
progress | 0–99, поки завдання виконується; 100 — лише тоді, коли аудіо готове. |
stage | Що відбувається зараз: queued (чекає на голосовий рушій), cold_start (запускаються потужності), awaiting_voice (голос готують до нової мови), rendering (озвучення), stitching (складання аудіо). Може бути null. Можуть з’явитися нові значення — незнайомі вважайте ознакою того, що завдання «ще виконується». Орієнтуйтеся на status, а не на stage. |
eta_seconds | Скільки приблизно секунд залишилося, якщо це відомо; інакше null. |
poll_after_seconds | Скільки чекати перед наступним запитом. null, коли завдання вже завершене. |
error_code, error_message | Чому завдання не вдалося — дивіться розділ «Помилки». |
// While it is running
{
"id": "0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12",
"status": "processing",
"progress": 42,
"stage": "rendering",
"eta_seconds": 35,
"characters": 68,
"credits_charged": null,
"paid_with": "credits",
"audio_url": null,
"poll_after_seconds": 7
}
// When it is ready
{
"id": "0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12",
"status": "completed",
"progress": 100,
"characters": 68,
"credits_charged": 68,
"paid_with": "credits",
"language": "en",
"audio_url": "https://cdn.tonvio.net/…",
"audio_expires_at": "2026-11-06T10:12:03.000Z",
"duration_seconds": 5.8,
"poll_after_seconds": null
}
// If it failed (the reserved characters are already back on your balance)
{
"status": "failed",
"error_code": "voice_language_mismatch",
"error_message": "The provider detected the text language as \"fr\", but this voice speaks only: en, de. …",
"error_details": { "text_language": "fr", "voice_languages": ["en", "de"] },
"poll_after_seconds": null
}Як чекати
- Найпростіше:
GET /speech/{id}?wait=60. Сервер тримає запит, доки завдання не завершиться або не мине 60 секунд, а тоді відповідає поточним станом. Повторюйте, доки стан не стане остаточним. - Звичайне опитування: запитуйте знову через
poll_after_seconds— ніколи не за фіксованим коротким таймером. - Багато завдань одразу:
GET /speech?status=queued,processingповертає всі незавершені завдання ключа одним запитом. - Зовсім без опитування: налаштуйте вебхук, і ми самі звернемося до вашого сервера.
# Long polling: the server answers as soon as the job finishes, or after 60 s at most.
curl "https://api.tonvio.net/api/v1/speech/JOB_ID?wait=60" -H "Authorization: Bearer tvk_your_key_here"
# Or list everything still running in one request
curl "https://api.tonvio.net/api/v1/speech?status=queued,processing&limit=100" -H "Authorization: Bearer tvk_your_key_here"Короткий текст зазвичай готовий за кілька секунд. Після періоду затишшя перше завдання може чекати 15–20 хвилин, поки запускаються потужності (стадія cold_start), а довга книжка займає більше часу. Не відмовляйтеся від завдання раніше ніж за дві години.
Отримання аудіо
- Коли
statusмає значенняcompleted, уaudio_urlє посилання на MP3 (44,1 кГц, 192 кбіт/с).duration_seconds— тривалість запису. - Посилання діє приблизно 6 годин. Кожен
GET /speech/{id}повертає свіже посилання, тож просто запитуйте знову, коли воно знадобиться. - Ми зберігаємо файл 30 днів після завершення завдання — дату вказано в
audio_expires_at. Після цьогоaudio_urlдорівнюєnull, аaudio_expired—true. Завантажте файл і зберігайте власну копію.
Символи й оплата
- 1 символ тексту = 1 кредит. Рахується все, що є в
text: літери, пробіли, розділові знаки, переноси рядків і теги в дужках. Більшість емодзі рахуються як 2 символи. - Поки в облікового запису діє підписка «Безліміт», API покривається нею: завдання не витрачають кредити й показують
paid_with: subscription. Коли підписка закінчиться, наступні завдання оплачуються кредитами (paid_with: credits); коли це станеться, показуєcredits.plan_period_endуGET /account. - Сервіс може працювати в режимі «лише кредити»: тоді підписка API не покриває і кожне завдання оплачується кредитами. Який режим діє, показує
credits.billing_modeуGET /account(subscriptionабоcredits_only); чи покриті завдання цього ключа підпискою просто зараз —credits.plan_covers_api. - Без підписки символи резервуються, коли завдання прийнято (
charactersу відповіді), і списуються, коли воно завершується (credits_charged). - Якщо завдання не вдалося, увесь резерв повертається. Якщо ви скасуєте завдання до завершення, резерв теж повернеться.
- Повтор із тим самим
Idempotency-Keyніколи не резервує символи вдруге. Запит, відхилений з помилкою, нічого не резервує. - Кредити для API беруться з придбаних пакетів символів. Бонусні символи (вітальний бонус, акції) діють лише у вебзастосунку, якщо ваш договір не передбачає іншого.
GET /accountпоказує, скільки цей ключ може витратити просто зараз:credits.api_spendable_chars(null, поки API покриває підписка). Якщо балансу не вистачає,POST /speechповертає402 insufficient_balanceз полямиneedіavailable.- Читання завдань, голосів і даних облікового запису ніколи не витрачає символів.
curl https://api.tonvio.net/api/v1/account -H "Authorization: Bearer tvk_your_key_here"
{
"credits": { "api_spendable_chars": 250000, "plan_unlimited": false, "spend_bucket": "purchased", … },
// With an active Unlimited subscription: "api_spendable_chars": null, "plan_unlimited": true,
// "plan_period_end": "2026-11-08T10:00:00.000Z" — jobs spend no credits until then
// In credits-only mode ("billing_mode": "credits_only") a subscription does not cover the API:
// "plan_covers_api": false and "api_spendable_chars" is always a number
"limits": { "max_chars_per_request": 200000, "active_slots": 3, "rate_per_min": 300, … },
"usage": { "key": { "active_jobs": 1, "queued_jobs": 0, "chars_today": 18340, … } },
…
}Ліміти
| Ліміт | Значення | Якщо перевищено |
|---|---|---|
| Запити | 300 на хвилину на ключ (короткі сплески допустимі) | 429 rate_limited — зачекайте Retry-After секунд |
| Текст одного завдання | limits.max_chars_per_request у GET /account | 400 text_too_long з полем max |
| Одночасні завдання | Слоти вашого тарифу, спільні з вебзастосунком; зайві завдання чекають у стані queued | 429 too_many_jobs, коли черга заповнена, — зачекайте Retry-After |
| Символи на день / місяць | Лише якщо їх задано для ключа (дні й місяці за UTC) | 429 quota_exceeded з полем resets_at |
Відповіді містять заголовки RateLimit-Limit, RateLimit-Remaining і RateLimit-Reset. Усі ліміти, що діють для вашого ключа, є в GET /account.
Безпечні повтори
Мережа іноді збоїть. Щоб повтор був безпечним, надсилайте Idempotency-Key: <any unique string> разом із POST /speech і передавайте той самий ключ, коли повторюєте запит.
- Той самий ключ, той самий запит → ви отримаєте початкове завдання (
200), а не нове. Нічого не списується двічі. - Той самий ключ, інший запит →
409 idempotency_mismatch. - Той самий ключ після того, як ви видалили це завдання, →
409 idempotency_key_consumed. Використайте новий ключ. - Без ключа кожен виклик створює нове завдання. Ми пам’ятаємо ключі 90 днів.
Повторюйте лише відповіді 429 і 5xx, витримавши затримку Retry-After. Інші помилки 4xx від повтору не зникнуть — спершу виправте запит.
Скасування й видалення
POST /speech/{id}/cancelзупиняє завдання в станіqueuedабоprocessing. Зарезервовані символи повертаються.DELETE /speech/{id}видаляє завдання та його аудіо, а якщо воно ще виконується — спершу скасовує його.- Коли звук уже повністю озвучено й лишилося тільки зберегти файл, завдання зупинити не можна: ви отримаєте
409 not_cancellableабо409 finalizing, і завдання завершиться як звичайно — з аудіо та звичайним списанням.409 job_busyозначає: спробуйте ще раз за кілька секунд. - Скасування не скасовує вже озвучену роботу. Якщо за добу скасовано понад мільйон уже озвучених символів, прийом нових завдань призупиняється до кінця цієї доби:
429 cancel_cooldownзRetry-After.
# Stop a job that is still queued or running — the reserved characters come back
curl -X POST https://api.tonvio.net/api/v1/speech/JOB_ID/cancel -H "Authorization: Bearer tvk_your_key_here"
# → { "id": "JOB_ID", "status": "canceled" }
# Delete a job and its audio (a running job is canceled first)
curl -X DELETE https://api.tonvio.net/api/v1/speech/JOB_ID -H "Authorization: Bearer tvk_your_key_here"
# → { "id": "JOB_ID", "deleted": true, "canceled": false, "idempotency_key_reusable": false }Вебхуки
Не потрібно питати про завдання — ми самі вам повідомимо. Налаштуйте вебхук для ключа у вебзастосунку (Сервіс API → ваш ключ → Вебхук): адресу https, потрібні події та секрет для підпису, який показується лише один раз.
| Подія | Коли |
|---|---|
speech.completed | Завдання завершено; у data є audio_url. |
speech.failed | Завдання не вдалося; у data є error_code і error_message. |
clone.completed | Клон голосу готовий; у data є voice_id. |
clone.failed | Не вдалося створити клон голосу. |
POST /your-endpoint HTTP/1.1
Host: your-server.example
Content-Type: application/json
User-Agent: tonvio-webhooks/1
X-Tonvio-Event: speech.completed
X-Tonvio-Delivery: 0f9d5f2e-4d1e-4a2b-9a3c-1f2e3d4c5b6a
X-Tonvio-Attempt: 1
X-Tonvio-Timestamp: 1789000000
X-Tonvio-Signature: t=1789000000,v1=6b1c9f0e…
{
"id": "0f9d5f2e-4d1e-4a2b-9a3c-1f2e3d4c5b6a",
"schema_version": 1,
"event": "speech.completed",
"object_id": "0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12",
"sent_at": "2026-10-07T10:12:03.114Z",
"data": {
"id": "0b3a7c11-8f2d-4e55-9a10-6c7b8d9e0f12",
"status": "completed",
"characters": 68,
"credits_charged": 68,
"paid_with": "credits",
"audio_url": "https://cdn.tonvio.net/…",
"error_code": null,
"error_message": null
}
}Перевірте підпис
Кожна доставка підписана: X-Tonvio-Signature: t=<time>,v1=<signature>, де підпис — це HMAC-SHA256 від <t>.<raw body> з вашим секретом, у шістнадцятковому вигляді. Перевіряйте сирі байти (а не повторно закодований JSON), відхиляйте доставки, у яких t відрізняється від вашого годинника більше ніж на 5 хвилин, і порівнюйте підписи за сталий час.
import crypto from "node:crypto";
import express from "express";
const SECRET = process.env.TONVIO_WEBHOOK_SECRET; // whsec_… shown once when you set it
const TOLERANCE_SECONDS = 300;
// Verify the RAW bytes. Parsing JSON and serializing it back changes the bytes,
// and the signature will then never match — this is the #1 integration mistake.
function verify(rawBody, header) {
const parts = {};
for (const piece of String(header || "").split(",")) {
const i = piece.indexOf("=");
if (i > 0) parts[piece.slice(0, i).trim()] = piece.slice(i + 1).trim();
}
const t = Number(parts.t);
if (!Number.isFinite(t) || !parts.v1) return false;
// Replay window: the timestamp is part of the signed string, so it cannot be moved.
if (Math.abs(Math.floor(Date.now() / 1000) - t) > TOLERANCE_SECONDS) return false;
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(parts.v1, "utf8");
// Constant-time compare: === on a hex string leaks the common prefix.
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
const app = express();
app.post("/tonvio-hook", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.get("X-Tonvio-Signature"))) return res.sendStatus(401);
const event = JSON.parse(raw);
// Deduplicate by event.id: a retry repeats the SAME delivery id and the same body.
// Answer 2xx first and do the slow work afterwards — we wait 10 s, then retry.
res.sendStatus(200);
queue.push(event);
});Правила доставки
- Відповідайте будь-яким
2xxпротягом 10 секунд; повільну роботу виконуйте вже після відповіді. - Якщо ми не отримаємо
2xx, то повторимо доставку: до 5 спроб упродовж приблизно 2,6 години. Відповідайте410 Gone, щоб зупинити повтори. - Доставка може прийти двічі й не в тому порядку. Пропускайте ідентифікатори, які ви вже обробили (
X-Tonvio-Delivery). - Скасування завдання не надсилає жодної події — ви ж самі його скасували.
Налагодження
POST /webhooks/test надсилає на вашу адресу тестову подію й показує, що ваш сервер відповів. GET /webhooks/deliveries показує справжні доставки з тим самим тілом, яке було підписано, а POST /webhooks/deliveries/{id}/requeue надсилає доставку ще раз.
# Where this key delivers and whether a signing secret is set
curl https://api.tonvio.net/api/v1/webhooks -H "Authorization: Bearer tvk_your_key_here"
# Send yourself a test event; 200 comes back even when your server answered 500 — read result.ok
curl -X POST https://api.tonvio.net/api/v1/webhooks/test -H "Authorization: Bearer tvk_your_key_here"
# → { "result": { "ok": false, "status": 500, "signed": true, "response_snippet": "…", "duration_ms": 214 } }
# The log of real deliveries, with the exact body that was signed
curl "https://api.tonvio.net/api/v1/webhooks/deliveries?state=dead&limit=20" -H "Authorization: Bearer tvk_your_key_here"
# Send one delivery again after you fixed your server
curl -X POST https://api.tonvio.net/api/v1/webhooks/deliveries/DELIVERY_ID/requeue -H "Authorization: Bearer tvk_your_key_here"Клонування голосу
Перетворіть запис власного голосу на приватний голос, яким можете користуватися лише ви. Ключу потрібне право clone.
- 1Надішліть
POST /voice-clonesяк форму (multipart/form-data) із заголовкомIdempotency-Key(тут він обов’язковий):name,consent, необов’язковоlanguages, а також файл у поліaudio— mp3, wav, m4a або webm, до 15 МБ. Текстові поля ставте перед файлом. - 2Перевіряйте
GET /voice-clones/{id}не частіше, ніж вказуєpoll_after_seconds. Створення голосу триває кілька хвилин. - 3Коли
statusстанеcompleted, передайте йогоvoice_idуPOST /speech, як і будь-який інший голос.
# 1. Order a private clone from your own recording.
# Text fields MUST come before the file, otherwise 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: 4c2f8e9a-1b7d-4e3a-8c6f-5d9b0a1e2f34" \
-F "name=My narrator" \
-F "consent=I confirm this is my own voice and I agree to have it cloned." \
-F "languages=en" \
-F "[email protected];type=audio/mpeg"
# → 202 { "id": "CLONE_ID", "status": "queued", "visibility": "private", "poll_after_seconds": 15 }
# 2. Check on it no more often than poll_after_seconds says; building takes minutes
curl https://api.tonvio.net/api/v1/voice-clones/CLONE_ID -H "Authorization: Bearer tvk_your_key_here"
# → { "status": "completed", "percent": 100, "voice_id": "VOICE_ID" }
# 3. Use it like any other voice
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: 9e1d7b3c-2a4f-4c8e-b6d0-3f5a7c9e1b2d" \
-d '{ "text": "Hello, this is my cloned voice.", "voice_id": "VOICE_ID" }'consent— заява завдовжки 20–500 символів про те, що ви маєте право на цей голос. Ми зберігаємо її разом із запитом. Клонуйте лише власний голос або голос, на використання якого маєте письмовий дозвіл.- Скільки клонів може створити ключ, указано в
limits.clone_limitу відповідіGET /account. DELETE /voice-clones/{id}видаляє клон, який вам більше не потрібен.
Помилки
Усі помилки мають однакову форму: HTTP-статус і тіло JSON з машиночитним кодом error, а для деяких кодів — ще й додаткові поля. Вирішуйте, що робити, за кодом, а не за текстом. Кожна відповідь має заголовок X-Request-Id — додавайте його, коли звертаєтеся до підтримки.
HTTP/1.1 402 Payment Required
X-Request-Id: req-8f1c2d
{ "error": "insufficient_balance", "feature": "api", "need": 120000, "available": 45000 }
HTTP/1.1 429 Too Many Requests
Retry-After: 10
{ "error": "too_many_jobs", "scope": "account", "limit": 3, "retry_after": 10 }Помилки запиту
| Статус | Код | Що робити |
|---|---|---|
| 400 | invalid_request | Тіло запиту неправильне — message пояснює, що саме. Виправте й надішліть знову. |
| 400 | text_too_long, text_too_short | Скоротіть текст (max) або додайте слів. |
| 401 | missing_api_key, invalid_api_key, api_key_expired | Перевірте ключ; якщо його відкликано або строк його дії минув, створіть новий. |
| 402 | insufficient_balance | Поповніть символи або оформіть «Безліміт» (need, available). |
| 403 | insufficient_scope | Ключу бракує права; required називає, якого саме. |
| 403 | voice_not_allowed | Цей голос використати не можна — виберіть інший. |
| 404 | not_found | Для цього ключа немає такого завдання чи голосу. |
| 409 | idempotency_mismatch, idempotency_key_consumed | Для нового запиту використайте новий Idempotency-Key. |
| 429 | rate_limited, create_busy, too_many_jobs, quota_exceeded | Зачекайте Retry-After (або до resets_at) і повторіть. |
| 429 | cancel_cooldown | Прийом нових завдань призупинено після численних скасувань уже озвученої роботи. Зачекайте Retry-After. |
| 504 | create_outcome_unknown | Не вдалося підтвердити, чи створено завдання; його job_id є у відповіді. Повторіть із тим самим Idempotency-Key (це безпечно в будь-якому разі) або перевірте GET /speech/{id}. |
| 5xx | generation_unavailable, service_busy, service_unavailable, delivery_unavailable, internal_error | Тимчасова проблема. Повторіть після Retry-After з тим самим Idempotency-Key. |
Коли завдання не вдалося
Завдання може бути прийняте, а згодом завершитися помилкою. Тоді GET /speech/{id} (і вебхук speech.failed) повертають status: failed, error_code та error_message, яке можна показати людям. Зарезервовані символи завжди повертаються.
| `error_code` | Значення | Повторювати? |
|---|---|---|
voice_language_mismatch | Текст написано мовою, якою цей голос не говорить (мову визначено за текстом). error_details називає обидві мови. | Ні — виберіть голос, який говорить цією мовою. |
voice_language_not_available | Голос зараз не може говорити цією мовою (або в нього немає версії для запитаної). | Ні — виберіть інший голос (або зачекайте, якщо error_details.variant_building дорівнює true). |
voice_not_found | Голос більше недоступний. | Ні — виберіть інший голос. |
text_rejected | У тексті немає слів для озвучення або є символи, які неможливо прочитати вголос. | Ні — виправте текст. |
synthesis_failed | Озвучення не вдалося з тимчасової причини. | Так, пізніше. |
queue_timeout, execution_timeout, provider_unavailable, voice_unavailable | Тимчасова нестача потужностей. | Так, пізніше. |
assembly_failed | Не вдалося зберегти аудіо. | Так. |
output_too_large | Аудіофайл вийшов би завеликим. | Розділіть текст на кілька завдань. |
generation_failed | Будь-яка інша причина. | Один раз; якщо помилка повториться, зверніться до підтримки й укажіть ідентифікатор завдання. |
Усі методи API
| Метод | Шлях | Що робить | Право |
|---|---|---|---|
POST | /speech | Створити завдання | speech |
GET | /speech/{id} | Стан завдання й посилання на аудіо (?wait= — щоб дочекатися) | speech:read |
GET | /speech | Завдання цього ключа, від найновіших (status, limit, cursor) | speech:read |
POST | /speech/{id}/cancel | Скасувати завдання | speech |
DELETE | /speech/{id} | Видалити завдання та його аудіо | speech |
GET | /voices | Голоси (language, gender, q, limit, offset) | voices |
GET | /voices/{id} | Один голос | voices |
GET | /voices/{id}/preview.mp3 | Послухати зразок голосу (працює без ключа) | — |
GET | /account | Баланс, ліміти й використання цього ключа | account |
GET | /webhooks | Налаштування вебхука цього ключа | account |
POST | /webhooks/test | Надіслати тестову подію | account |
GET | /webhooks/deliveries | Журнал доставок | account |
POST | /webhooks/deliveries/{id}/requeue | Доставити ще раз | account |
POST | /voice-clones | Клонувати голос із запису | clone |
GET | /voice-clones/{id} | Стан клону | clone |
GET | /voice-clones | Клони цього ключа | clone |
DELETE | /voice-clones/{id} | Видалити клон | clone |
Файл OpenAPI описує кожен метод, поле й код помилки, зокрема й ті кілька, що залишені лише для старих інтеграцій (наприклад, /voices/{id}/warm). Передайте його генераторові коду, Postman чи AI-асистентові.
Стабільність
- Версія 1 лише розширюється: ми додаємо поля, значення й методи, але ніколи не видаляємо й не перейменовуємо те, що тут задокументовано.
- Тож ігноруйте незнайомі поля, а невідомі значення
stageчиerror_codeобробляйте як загальні. - Усі дати — у форматі ISO 8601 за UTC. Відповіді ніколи не кешуються (
Cache-Control: no-store).
Що розробники питають насамперед
Чи можна прочитати документацію без акаунта?
Так. Ця сторінка, файл Markdown і специфікація OpenAPI відкриті для всіх. Акаунт потрібен лише для того, щоб створити ключ і витрачати символи.
API синхронний?
Ні — озвучення триває від секунд до хвилин, тому POST /speech одразу повертає id завдання. Далі або викликайте GET /speech/{id}?wait=60 — відповідь прийде, щойно завдання буде готове, — або питайте через poll_after_seconds, або налаштуйте вебхук, і ми повідомимо вас самі.
Скільки коштує виклик API?
З активною підпискою «Безліміт» — нічого: API покриває підписка. Без неї ви платите символами: один символ тексту — один кредит, із придбаних пакетів символів. Символи резервуються, коли завдання прийнято, списуються, коли воно готове, і повертаються повністю, якщо воно не вдалося або ви його скасували. Читання завдань і голосів безкоштовне. Ціни — на сторінці тарифів.
Скільки голосів і мов?
Понад 18 000 голосів. Фільтруйте GET /voices за мовою: для кожного голосу вказано мови, якими він говорить одразу, і ті, які він опанує під час першого завдання.
Чи можна керувати емоціями та паузами?
Так. Пишіть вказівки на кшталт [whisper], [laughing] чи [pause] у квадратних дужках просто в тексті, а швидкість мовлення й паузи задавайте в settings.voice.
Які ліміти на запити?
Типово 300 запитів на хвилину на ключ, короткі сплески допустимі. Скільки завдань виконується одночасно й якої довжини може бути один текст, залежить від тарифу; точні цифри для вашого ключа — у GET /account.
Як зробити повтори безпечними?
Надсилайте заголовок Idempotency-Key з кожним POST /speech і повторюйте з тим самим ключем. Той самий ключ поверне початкове завдання, а не створить і не оплатить друге.
Чи можна клонувати голос через API?
Так, якщо в ключа є право clone: завантажте запис власного голосу, зачекайте кілька хвилин і використовуйте новий voice_id як будь-який інший голос.
Скільки зберігається аудіо?
Посилання на завантаження діє близько 6 годин, і кожен запит статусу видає свіже. Сам файл зберігається 30 днів — завантажте його й зберігайте власну копію.
Почніть з одного запиту
Створіть ключ у кабінеті — і до першого озвучення залишиться хвилина. З «Безлімітом» API покриває підписка, без нього ви платите лише за озвучені символи.
Уперше в Tonvio? Почніть з огляду озвучення тексту.