Перейти до вмісту
tonvio

API озвучення тексту

Tonvio API: жива мова для вашого продукту

Один HTTP API, щоб перетворювати текст на мовлення: понад 18 000 голосів, теги емоцій, клонування голосу та вебхуки. Увесь довідник відкритий — читайте тут, завантажте його в Markdown або OpenAPI й оцініть інтеграцію ще до реєстрації.

API працює на тому самому рушії, що й кабінет: ті самі голоси та можливості генерації, та сама черга, той самий готовий MP3.

Візьміть увесь довідник із собою: файл Markdown для команди чи AI-асистента або специфікацію OpenAPI для генераторів коду та HTTP-клієнтів.

Завантажити OpenAPI (JSON)

Перетворюйте текст на природне мовлення через HTTP. Ви надсилаєте текст і голос — Tonvio повертає MP3. Завдання виконуються асинхронно: створіть завдання, дочекайтеся, поки воно завершиться, і завантажте аудіо. Усе, що описано нижче, працює з будь-яким HTTP-клієнтом. Увесь довідник також доступний як файл OpenAPI і як один файл Markdown.

Базовий URL: https://api.tonvio.net/api/v1

Швидкий старт

  1. 1Створіть ключ API у вебзастосунку: Сервіс API → Ключі API. Ключ показується лише один раз — збережіть його на своєму сервері.
  2. 2Виберіть голос через GET /voices і скопіюйте його id.
  3. 3Створіть завдання через POST /speech: передайте текст, voice_id і, за бажанням, language. Ви одразу отримаєте id завдання.
  4. 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 — обидва способи працюють однаково. Ключ — це пароль до вашого балансу: зберігайте його на своєму сервері й ніколи не вбудовуйте в сайт чи мобільний застосунок.

HTTP
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
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); після цього завдання стартують одразу.
originvendor — готовий голос із каталогу; 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 символів). Використовуйте щось одне.
JSON
{
  "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
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. Ці три стани остаточні.

ПолеЗначення
statusqueued — чекає на вільний слот; processing — озвучується; completed, failed, canceled — завершено.
progress0–99, поки завдання виконується; 100 — лише тоді, коли аудіо готове.
stageЩо відбувається зараз: queued (чекає на голосовий рушій), cold_start (запускаються потужності), awaiting_voice (голос готують до нової мови), rendering (озвучення), stitching (складання аудіо). Може бути null. Можуть з’явитися нові значення — незнайомі вважайте ознакою того, що завдання «ще виконується». Орієнтуйтеся на status, а не на stage.
eta_secondsСкільки приблизно секунд залишилося, якщо це відомо; інакше null.
poll_after_secondsСкільки чекати перед наступним запитом. null, коли завдання вже завершене.
error_code, error_messageЧому завдання не вдалося — дивіться розділ «Помилки».
JSON
// 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 повертає всі незавершені завдання ключа одним запитом.
  • Зовсім без опитування: налаштуйте вебхук, і ми самі звернемося до вашого сервера.
cURL
# 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
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 /account400 text_too_long з полем max
Одночасні завданняСлоти вашого тарифу, спільні з вебзастосунком; зайві завдання чекають у стані queued429 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.
cURL
# 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Не вдалося створити клон голосу.
HTTP
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 надсилає доставку ще раз.

cURL
# 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. 1Надішліть POST /voice-clones як форму (multipart/form-data) із заголовком Idempotency-Key (тут він обов’язковий): name, consent, необов’язково languages, а також файл у полі audio — mp3, wav, m4a або webm, до 15 МБ. Текстові поля ставте перед файлом.
  2. 2Перевіряйте GET /voice-clones/{id} не частіше, ніж вказує poll_after_seconds. Створення голосу триває кілька хвилин.
  3. 3Коли status стане completed, передайте його voice_id у POST /speech, як і будь-який інший голос.
cURL
# 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
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 }

Помилки запиту

СтатусКодЩо робити
400invalid_requestТіло запиту неправильне — message пояснює, що саме. Виправте й надішліть знову.
400text_too_long, text_too_shortСкоротіть текст (max) або додайте слів.
401missing_api_key, invalid_api_key, api_key_expiredПеревірте ключ; якщо його відкликано або строк його дії минув, створіть новий.
402insufficient_balanceПоповніть символи або оформіть «Безліміт» (need, available).
403insufficient_scopeКлючу бракує права; required називає, якого саме.
403voice_not_allowedЦей голос використати не можна — виберіть інший.
404not_foundДля цього ключа немає такого завдання чи голосу.
409idempotency_mismatch, idempotency_key_consumedДля нового запиту використайте новий Idempotency-Key.
429rate_limited, create_busy, too_many_jobs, quota_exceededЗачекайте Retry-After (або до resets_at) і повторіть.
429cancel_cooldownПрийом нових завдань призупинено після численних скасувань уже озвученої роботи. Зачекайте Retry-After.
504create_outcome_unknownНе вдалося підтвердити, чи створено завдання; його job_id є у відповіді. Повторіть із тим самим Idempotency-Key (це безпечно в будь-якому разі) або перевірте GET /speech/{id}.
5xxgeneration_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? Почніть з огляду озвучення тексту.