Транскрибация
через API
— за 30 строк кода
REST-эндпоинты с JSON-ответами и авторизацией через X-API-Key.
Загружайте файлы, получайте текст, саммари, субтитры SRT/VTT — в свой бэкенд.
3 шага от файла до субтитров
Загрузка, поллинг статуса, экспорт SRT — всё через 3 curl-запроса.
# 1. Загрузить файл curl -X POST https://wonderscribe.pro/api/v1/transcribe \ -H "X-API-Key: ws_..." \ -F "file=@interview.mp3" \ -F "language=ru" # → {"task_id": "abc-123", "status": "pending"} # 2. Поллить статус (раз в 10-15 секунд) curl -H "X-API-Key: ws_..." \ https://wonderscribe.pro/api/v1/tasks/abc-123 # → {"status": "completed", "progress": 100} # 3. Экспортировать субтитры SRT curl -H "X-API-Key: ws_..." \ "https://wonderscribe.pro/api/v1/tasks/abc-123/export?format=srt" # → {"content": "1\n00:00:00,000 --> 00:00:04,200\n...", "format": "srt"}
16 методов для всего флоу
| Method | Endpoint | Назначение |
|---|---|---|
| GET | /api/v1/account |
Инфо об аккаунте, план, использовано минут |
| POST | /api/v1/transcribe |
Загрузить файл + старт транскрибации Нет денег на балансе → status: "awaiting_balance", файл ждёт пополнения |
| POST | /api/v1/transcribe-url |
Транскрибация по ссылке: YouTube, VK Видео, RuTube, Дзен, Яндекс.Диск или прямой URL на аудио/видео |
| GET | /api/v1/tasks |
Список задач (пагинация: limit, offset, status) |
| GET | /api/v1/tasks/{id} |
Статус задачи + progress % |
| GET | /api/v1/tasks/{id}/result |
Полный результат: текст, сегменты, саммари |
| GET | /api/v1/tasks/{id}/export?format=… |
Экспорт: txt · srt · vtt · json |
| POST | /api/v1/tasks/{id}/cancel |
Отмена pending/processing задачи |
| POST | /api/v1/api-key/generate |
Ротация ключа (старый — invalidated) |
| POST | /api/v1/meeting |
AI-бот на встречу (Zoom/Meet/Teams/Telemost): сейчас или по расписанию Пустой баланс → 402: бот не отправляется |
| GET | /api/v1/meeting/{bot_id} |
Статус бота + task_id записи когда готово |
| DELETE | /api/v1/meeting/{bot_id} |
Отменить запланированную или остановить идущую запись |
| GET | /api/v1/dictionary |
Словарь терминов аккаунта — что применяется к расшифровкам |
| PUT | /api/v1/dictionary |
Заменить словарь целиком |
| POST | /api/v1/dictionary |
Добавить термины к существующим |
| DELETE | /api/v1/dictionary |
Очистить свой словарь |
AI-бот на встречу
Из вашей CRM отправьте бота на онлайн-встречу — он подключится, запишет и автоматически расшифрует. Заберите результат поллингом GET /api/v1/meeting/{bot_id} → /api/v1/tasks/{task_id}/result или получите webhook, когда транскрипт готов. Тело запроса — JSON или form-данные (подходит для amoCRM, Bitrix24, Make, Zapier «из коробки»).
| Поле | Тип | Default | Описание |
|---|---|---|---|
meeting_url * | string | Ссылка на встречу (Zoom, Google Meet, Teams, Telemost, Webex) | |
consent_acknowledged * | bool | Подтверждение, что участники уведомлены о записи и согласны на обработку ПДн (152-ФЗ) | |
start_at | ISO 8601 | сейчас | Время запуска (напр. 2026-06-02T15:00:00+03:00). В будущем → бот зайдёт по расписанию. Макс. +30 дней |
webhook_url | https URL | Callback, куда придёт POST когда транскрипт готов. Тело подписано HMAC-SHA256 заголовком X-WS-Signature ключом webhook_secret из ответа |
* — обязательное поле · ответ: {bot_id, status, scheduled_start_at?, webhook_secret?}
Словарь узких терминов
Термины из словаря применяются ко всем расшифровкам аккаунта — и к тем, что приходят через API. Это помогает, когда в записях звучат имена, аббревиатуры и слова, которых нет в обычной речи.
| Запрос | Что делает |
|---|---|
GET /api/v1/dictionary | Текущий список: terms, count, chars, limit_chars, limit_terms, effective_terms, ignored_terms |
PUT /api/v1/dictionary | Заменить целиком. Тело: {"terms": ["ЭКГ", "Дентиум"]} либо {"text": "ЭКГ, Дентиум"} |
POST /api/v1/dictionary | Добавить к существующим, дубликаты отбрасываются |
DELETE /api/v1/dictionary | Очистить свой словарь |
POST /api/v1/dictionary/preview | Проверка без пересчёта: {"task_id": "...", "terms": [...]} — что новый список изменил бы в уже готовой расшифровке. Ничего не сохраняет, минуты не тратит |
limit_terms | Сколько терминов реально применяется к записи (первые по порядку). Хранить можно больше — ignored_terms покажет, какие в обработку не пойдут |
curl -X PUT https://wonderscribe.pro/api/v1/dictionary \
-H "X-API-Key: $WS_KEY" -H "Content-Type: application/json" \
-d '{"terms": ["трохантерит", "гистероскопия", "Дентиум"]}'
# разовые термины только для одной записи — прямо в загрузке:
curl -X POST https://wonderscribe.pro/api/v1/transcribe \
-H "X-API-Key: $WS_KEY" \
-F "file=@lecture.mp3" -F "prompt=трохантерит, вертлужная впадина"
Разделители в text — запятая, точка с запятой, перенос строки. Лимит словаря — 20 000 символов; при превышении приходит 413, список не обрезается молча.
У команды участник без своего словаря наследует список владельца — в ответе это поле inherited_from_team_owner.
Параметры загрузки
| Поле | Тип | Default | Описание |
|---|---|---|---|
file * | file | Аудио/видео файл (.mp3, .wav, .mp4, .mov, .m4a, .ogg, .flac, .webm и др.) | |
language | string | ru | ISO-код языка. 99 поддерживаемых |
num_speakers | int | auto | Кол-во спикеров (1-6). Если известно — точность выше |
prompt | string | Имена, термины, аббревиатуры — для повышения точности | |
webhook_url | https URL | Куда прислать POST, когда расшифровка готова — чтобы не опрашивать статус. Тело подписано HMAC-SHA256 в заголовке X-WS-Signature ключом webhook_secret из ответа | |
domain | string | legal / medical / tech — для domain-prompts | |
summary_template | string | general | 92 шаблона: meeting / lecture / interview / podcast / др. |
* — обязательное поле
Что происходит с задачей
POST /transcribe возвращает task_id сразу и не ждёт расшифровки.
Дальше опрашивайте GET /tasks/{id} — раз в 5–10 секунд достаточно,
обработка часа записи занимает около 5 минут.
| status | Что значит | Что делать |
|---|---|---|
pending |
Принята, ждёт свободный GPU. В ответе есть queue_position и queue_total |
Продолжать опрос |
pendingawaiting_balance: true |
Денег на API-балансе не хватило. Файл сохранён, в очередь не поставлен | Пополнить баланс — расшифровка стартует сама в течение нескольких минут. Повторно загружать файл не нужно |
processing |
Идёт обработка. progress — проценты, stage — текущий этап |
Продолжать опрос |
completed |
Готово | GET /tasks/{id}/result или /export?format=srt |
failed |
Ошибка. Причина — в поле error |
Минуты за упавшую задачу не списываются |
Поле queue_state уточняет состояние: working · queued ·
not_started · done · awaiting_balance.
Повторные отправки — заголовок Idempotency-Key.
Передайте свой уникальный ключ в POST /transcribe: при повторе с тем же ключом
вернётся тот же task_id, а не вторая задача. Спасает при ретраях по таймауту —
загрузка большого файла легко переживает разрыв соединения уже после того, как сервер её принял.
Сколько ждать расшифровку
Обработка не потоковая: файл считается целиком, и текст приходит одним ответом. Ниже — замер по реальным задачам за 30 дней.
| Длина записи | Обычно | В 9 случаях из 10 |
|---|---|---|
| до 1 минуты | ~30 секунд | до 1 минуты |
| 1–10 минут | ~1,5 минуты | до 2,5 минут |
| 10–60 минут | ~7 минут | до 11 минут |
| больше часа | ~13 минут | до 24 минут |
На коротких записях время почти не зависит от длины: и 4 секунды, и 20 занимают примерно одинаково — это постоянные расходы на запуск, а не пропорция.
Указано время самой обработки; когда все GPU заняты длинными файлами, добавляется ожидание в очереди — в редких случаях до нескольких минут.
Чтобы не опрашивать статус, передайте webhook_url при загрузке — пришлём POST, как только текст готов.
Что вернётся, если что-то не так
Тело ошибки — {"detail": {"error_code": "...", "message": "..."}}.
Ветвитесь по error_code, а не по тексту: текст может меняться.
| HTTP | error_code | Когда |
|---|---|---|
| 401 | — | Ключ не передан, недействителен или API не включён в кабинете |
| 402 | api_balance_exhausted | Нет денег на балансе при отправке бота на встречу. Для POST /transcribe отказа нет — задача ждёт пополнения |
| 409 | — | Параллельный запрос с тем же Idempotency-Key ещё выполняется. Повторить чуть позже |
| 413 | audio_too_long_api_balance | Запись длиннее 4 часов. Разбейте на части |
| 413 | — | Файл больше 8 ГБ |
| 429 | — | Больше 20 задач одновременно в работе. Дождитесь завершения части |
| 503 | service_unavailable | Временный сбой. Повторить с экспоненциальной задержкой |
Лимиты. Файл до 8 ГБ и до 4 часов;
одновременно в работе до 20 задач на аккаунт.
Запросов в минуту на ключ: /transcribe — 10, /meeting — 20,
регенерация саммари — 5, остальные (чтение статуса, результата, словарь) — 60. Превышение — 429.
Нужны другие пороги — напишите.
Полный end-to-end пример
Загрузка → поллинг → результат + субтитры. Не хотите опрашивать — передайте webhook_url при загрузке и ждите POST с task_id.
import requests, time API_KEY = "ws_..." BASE = "https://wonderscribe.pro/api/v1" HEADERS = {"X-API-Key": API_KEY} # 1) Upload with open("interview.mp3", "rb") as f: r = requests.post( f"{BASE}/transcribe", headers=HEADERS, files={"file": f}, data={"language": "ru", "num_speakers": 2}, ) task_id = r.json()["task_id"] print("task_id:", task_id) # 2) Poll status while True: s = requests.get(f"{BASE}/tasks/{task_id}", headers=HEADERS).json() print(s["status"], s.get("progress", 0), "%") if s["status"] in ("completed", "failed"): break time.sleep(10) # 3) Get text + SRT result = requests.get(f"{BASE}/tasks/{task_id}/result", headers=HEADERS).json() print(result["text"]) srt = requests.get(f"{BASE}/tasks/{task_id}/export?format=srt", headers=HEADERS).json() with open("out.srt", "w") as f: f.write(srt["content"])
// Node.js 18+ — fetch и FormData уже встроены import fs from "node:fs"; const API_KEY = "ws_..."; const BASE = "https://wonderscribe.pro/api/v1"; const H = { "X-API-Key": API_KEY }; // 1) Загрузка const fd = new FormData(); fd.append("file", new Blob([fs.readFileSync("interview.mp3")]), "interview.mp3"); fd.append("language", "ru"); fd.append("num_speakers", "2"); let r = await fetch(`${BASE}/transcribe`, { method: "POST", headers: H, body: fd }); const { task_id, status } = await r.json(); // Денег не хватило — файл уже сохранён, задача поедет после пополнения if (status === "awaiting_balance") console.warn("ждём пополнения баланса"); // 2) Поллинг let s; do { await new Promise((ok) => setTimeout(ok, 5000)); s = await (await fetch(`${BASE}/tasks/${task_id}`, { headers: H })).json(); console.log(s.status, s.progress ?? 0, "%"); } while (!["completed", "failed"].includes(s.status)); // 3) Результат и субтитры const res = await (await fetch(`${BASE}/tasks/${task_id}/result`, { headers: H })).json(); console.log(res.text.slice(0, 300)); const srt = await (await fetch(`${BASE}/tasks/${task_id}/export?format=srt`, { headers: H })).json(); fs.writeFileSync("interview.srt", srt.content);
Отдельный ₽-баланс
REST API — не часть тарифов (Старт/Базовый/Профи/Команда). Отдельный prepaid-баланс, посекундная тарификация, прогрессивная шкала: чем больше минут за месяц — тем дешевле. Диаризация, пунктуация и таймкоды по фразам включены в цену; посадочного платежа нет.
| Объём в месяц | ₽/мин |
|---|---|
| первые 10 000 мин | 0,50 ₽ |
| 10 000 – 50 000 мин | 0,44 ₽ |
| 50 000 – 200 000 мин | 0,40 ₽ |
| свыше 200 000 мин | 0,36 ₽ |
Ступени считаются за календарный месяц, переход между ними — автоматический. Минимальное пополнение — 500 ₽.
Кончились деньги — файл не теряется.
POST /transcribe вернёт status: "awaiting_balance" и сохранит загрузку:
расшифровка начнётся сама в течение нескольких минут после пополнения, повторно загружать не нужно.
Опрашивать — обычным GET /tasks/{id} (там будет awaiting_balance: true).
Потоковое распознавание (реальное время, живые звонки) — скоро. Сейчас доступна пакетная обработка: загруженный файл или готовая запись.