API калибровки таймкодов текста
Это не распознавание речи. Текст всегда берётся из вашего исходного текста песни; мы лишь ставим каждый символ в правильный момент — поэтому «сдвиг текста из-за неверно услышанных слов» невозможен.
Описание эндпоинта
Он выравнивает готовый текст песни по аудио и возвращает таймкоды по строкам и по символам — для подсветки по символам в стиле караоке, прокрутки текста и генерации субтитров.
Эндпоинт: POST /api/music/lyrics-align
Аутентификация: см. Аутентификация; заголовок — Authorization: Bearer YOUR_API_KEY
Оплата: за вызов (сейчас по умолчанию 0,1 ¥ за вызов); если у песни платформы отмечено разделение вокала, дополнительно один раз списывается по цене платформы «разделение вокала/аккомпанемента». Фактически списанная сумма следует текущей конфигурации цен в бэк-офисе.
Среднее время: 5–15 секунд (зависит от длины аудио и от того, нужно ли разделять вокал на месте)
Зачем нужна калибровка: таймкоды со стороны генерации не всегда точны
API текста с таймкодами возвращает таймкоды по словам прямо со стороны генерации, и они неточны:
- повторы, плотные гармонии, рэп и длинные ноты часто дают общий сдвиг или смещение отдельных символов;
- у части песен отсутствует кусок в начале (например, чистое интро, и первый фрагмент начинается только на пятнадцатой секунде);
- некоторые строки трудно привязать к явному временному якорю.
Калибровка заново делает выравнивание по времени на основе вашего собственного текста: правится только время, текст — никогда, что подходит для случаев, где важно «попадание» (подсветка по символам как в KTV, послоговые субтитры, оценка пения).
Рекомендуемый порядок (три шага, лучший результат)
- Сначала извлеките вокал (разделение вокала/аккомпанемента) и выравнивайте только вокальную дорожку — аккомпанемент и гармонии больше не мешают. Основание по измерениям: при построчном сравнении «полный микс против вокальной дорожки» одной песни 5 из 28 строк различались более чем на 0,25 с (максимум — 2,64 с); два независимых движка расходились в среднем на 0,239 с на вокальной дорожке и на 0,508 с на полном миксе — почти вдвое хуже согласие. Поэтому для подсветки по символам (KTV/субтитры) её стоит включать. (Точный выигрыш сейчас перемеряется на новом движке.)
- Дайте полный текст: вся песня, построчно, один к одному с аудио. Пропущенная фраза или раздел сдвигает все последующие строки; не вставляйте в текст таймкоды вида
[00:23.82](они игнорируются автоматически). - Для песни платформы достаточно поставить
vocal_separate: true, и мы сделаем «разделение + выравнивание» за один шаг.
Три способа использования
| Способ | Что отправлять | Откуда текст | Разделение вокала |
|---|---|---|---|
| Внешняя песня · публичный URL | audio_url + текст |
Вы предоставляете | ❌ Не поддерживается |
| Внешняя песня · прямая загрузка локального файла (новое в 2026-10) | audio_object_key + текст |
Вы предоставляете | ❌ Не поддерживается |
| Песня платформы (созданная на этом сайте) | song_id |
Мы берём из записи песни | ✅ Опционально |
Как связаны параметры (ошибаются здесь чаще всего — читайте внимательно)
- Отправляете
song_id→ путь песни платформы: не отправляйте дополнительноlyrics/audio_url/audio_object_key; - Отправляете
audio_urlилиaudio_object_key→ путь внешней песни: только одно из двух, и не отправляйтеsong_id; vocal_separateработает только для песен платформы; для внешней песни он отклоняется (аудио не у нас, разделить нельзя).
Новое: прямая загрузка локального файла (аудио не проходит через наши серверы)
Используйте, когда аудио лежит у вас на машине и вы не хотите сначала выкладывать его в собственное публичное хранилище. Два шага:
POST /api/music/lyrics-align/upload-ticketсfile_name/file_size/content_typeи получитеput_url(подписанный URL без долгоживущих учётных данных) иobject_key;- Сделайте
PUTфайла наput_url, передавcontent_typeиз ответа ровно как есть (он участвует в подписи; один изменённый символ даёт 403); - При отправке выравнивания передавайте
audio_object_keyвместоaudio_url.
Учётные данные одноразовые: после завершения выравнивания (успешно или нет) сервер удаляет объект, и повторное использование того же key даёт ошибку.
Новое: текст теперь необязателен
Раньше для внешней песни текст был обязателен. Теперь его можно не передавать: сервер сначала выполняет распознавание и использует распознанный текст как текст песни.
Об этом нужно сказать прямо: текст тогда определяется результатом распознавания, отдельные символы могут быть неверны. Такие ответы помечаются
lyrics_source: "asr", confidence: "low" и, в warnings[],
{"code":"no_reference_lyrics"}.
Для подсветки по символам / KTV обязательно давайте исходный текст.
Параметры запроса
Headers
| Параметр | Тип | Обязательно | Примечания |
|---|---|---|---|
| Authorization | string | да | Bearer YOUR_API_KEY |
| Content-Type | string | да | application/json |
Параметры тела
| Параметр | Тип | Обязательно | По умолчанию | Примечания |
|---|---|---|---|---|
| song_id | string | одно из двух | - | ID песни платформы |
| lyrics | string | нет | - | Исходный текст, строки через \n. Теперь можно не передавать |
| audio_url | string | одно из двух для внешних | - | Адрес аудио; должен быть публично скачиваемым |
| audio_object_key | string | одно из двух для внешних | - | Ключ объекта прямой загрузки (сначала вызовите /lyrics-align/upload-ticket) |
| vocal_separate | boolean | нет | false |
Разделять ли вокал сначала (только песни платформы) |
| language | string | нет | автоопределение | Chinese / English и т. п.; без значения определяется по тексту |
| granularity | string | нет | both |
both / line (без вывода по символам) / word |
| sections | string | нет | keep |
keep сохраняет метки вида [Chorus] (они не входят в таймлайн) / drop |
| format | string | нет | json |
json / lrc / lrc-word (последние два дополнительно возвращают текст lrc и lrc_word) |
Ограничения
| Пункт | Ограничение |
|---|---|
| Длительность аудио | ≤ 6 минут (больше — ошибка и полный возврат) |
| Источник аудио | Должен быть публично скачиваемым; адреса, требующие входа, не принимаются |
| Формат текста | Простой текст, строки через \n; метки разделов вида [Verse] или 【主歌】 сохраняются как есть, но не входят в таймлайн |
| Пакеты | Одна песня за вызов; для нескольких делайте свой цикл |
| Хранение результата | Этот эндпоинт ничего не сохраняет; сохраняйте результат сами |
Примеры запроса
Внешняя песня (текст + адрес аудио)
curl -X POST https://www.suno-api.io/api/music/lyrics-align \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lyrics": "Иду по холодным улицам\nНочной ветер гладит глаза\nВремя течёт как вода",
"audio_url": "https://your-bucket.example.com/song.mp3"
}'
Песня платформы (без разделения)
curl -X POST https://www.suno-api.io/api/music/lyrics-align \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"song_id": "17e52af7-b5df-4877-a98a-dce94e0e6aa2"}'
Песня платформы + разделение вокала (рекомендуется для караоке)
curl -X POST https://www.suno-api.io/api/music/lyrics-align \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"song_id": "17e52af7-b5df-4877-a98a-dce94e0e6aa2", "vocal_separate": true}'
Ответ
Успешный ответ
{
"success": true,
"lyrics_source": "platform",
"vocal_separate": false,
"coverage": 0.935,
"exact": 196,
"fuzzy": 4,
"total": 214,
"lines": [
{
"text": "Иду по холодным улицам",
"start": 21.66,
"end": 24.06,
"chars": [
{"char": "И", "start": 21.66, "end": 21.74, "word": true},
{"char": "д", "start": 21.98, "end": 22.22, "word": true}
]
}
]
}
Описание полей
| Поле | Тип | Примечания |
|---|---|---|
success |
boolean | Успешен ли запрос |
lyrics_source |
string | platform = текст из песни платформы; user = текст предоставили вы; asr = текст не передан, символы из распознавания |
vocal_separate |
boolean | Проходил ли в этот раз разделение вокала |
confidence |
string | high / low (при lyrics_source=asr всегда low) |
warnings[] |
array | Предупреждения о деградации; без текста содержит {"code":"no_reference_lyrics"} |
coverage |
number | Качество выравнивания: доля символов с явным временным якорем (0–1) |
exact / fuzzy / total |
number | точных символов / омофонных / значимых символов текста |
lines[].text |
string | Одна строка текста, символ в символ равна вашему оригиналу |
lines[].start / end |
number | начало и конец строки (секунды) |
lines[].section |
boolean | является ли строка меткой раздела (true = без времени, без подсветки) |
lines[].chars[] |
array | время по символам: char, start/end в секундах, word — входит ли в таймлайн (знаки препинания false) |
words[] |
array | плоский список символов: text / start / end / line; не возвращается при granularity=line |
lrc |
string | построчный LRC-текст (только при format=lrc или lrc-word) |
lrc_word |
string | расширенный посимвольный LRC с тегами <mm:ss.xx> внутри строки (только при format=lrc-word) |
Как использовать coverage (три уровня)
| coverage | Рекомендуемое применение |
|---|---|
| ≥ 0,9 | можно делать подсветку по символам (караоке) |
| 0,8 – 0,9 | смешивать посимвольно и построчно либо подсвечивать только строки, а символы держать как ориентир |
| < 0,8 | только подсветка строк (повторы, плотные гармонии, диалекты и рэп часто выходят низко) |
Состояние и время выполнения
| Сценарий | Измеренное время |
|---|---|
| Песня платформы · без разделения | около 4,5 с |
| Песня платформы · разделение (вокальная дорожка уже есть) | около 11 с |
| Песня платформы · разделение (нужно разделять на месте) | около 25 с, максимум около 1 минуты |
| Внешняя песня · текст + адрес аудио | около 5–15 с (зависит от длины) |
Лимит одного запуска на сервере — 8 минут; установите тайм-аут клиента 120 секунд или больше.
Ответы с ошибкой
{
"success": false,
"message": "音频超过 6 分钟上限(实际 7.2 分钟)"
}
| Сценарий | Списывается? |
|---|---|
| Нет аудио (ни одно из трёх) | Не списывается (обработка не началась) |
audio_url и audio_object_key одновременно |
Не списывается (обработка не началась) |
| Объект прямой загрузки ещё не появился / уже удалён | Не списывается (обработка не началась) |
Некорректные granularity / sections / format |
Не списывается (обработка не началась) |
| Песни платформы нет / она не принадлежит текущему аккаунту | Не списывается (обработка не началась) |
| Параметры отсутствуют или неверны | Не списывается (обработка не началась) |
| Аудио длиннее лимита 6 минут | Полный возврат |
| Аудио нельзя скачать (не публичный адрес / нужен вход) | Полный возврат |
| Текст не передан, и распознавание на сервере не удалось | Полный возврат |
| Отмечено разделение вокала, но нет пригодной вокальной дорожки | Полный возврат (включая плату за разделение) |
| Сбой или тайм-аут сервиса выравнивания | Полный возврат |
Принцип: нет итогового результата — полный возврат. Списание происходит только когда таймкоды по символам реально возвращены, а возврат делается автоматически, без заявки.
Оплата
| Пункт | Когда списывается | Примечания |
|---|---|---|
| Калибровка таймкодов текста (база) | каждый раз | за вызов (сейчас по умолчанию 0,1 ¥ за вызов); фактическая сумма следует конфигурации цен в бэк-офисе |
| Надбавка за транскрипцию | только если текст не передан | серверу нужно сначала выполнить распознавание речи — расход, которого нет у уровня «с текстом», поэтому цена отдельная |
| Разделение вокала (опционально, только песни платформы) | при отметке vocal_separate |
один раз по текущей цене «разделение вокала/аккомпанемента», расчёт вместе с этим эндпоинтом |
Комбинация:
к оплате = базовая цена + (текст не передан ? надбавка за транскрипцию : 0) + (разделение отмечено ? цена разделения : 0)
Плата за разделение вокала берётся один раз: если вы уже разделили песню и вокальная дорожка доступна сразу, её берёт этот эндпоинт; если разделять нужно на месте — берёт шаг разделения.
Полный пример (Python)
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://www.suno-api.io"
def align_lyrics(song_id=None, lyrics=None, audio_url=None, vocal_separate=False):
payload = {}
if song_id:
payload["song_id"] = song_id
payload["vocal_separate"] = vocal_separate
else:
payload["lyrics"] = lyrics
payload["audio_url"] = audio_url
r = requests.post(
f"{BASE}/api/music/lyrics-align",
headers={"Authorization": f"Bearer {API_KEY}"},
json=payload,
timeout=600,
)
data = r.json()
if not data.get("success"):
raise RuntimeError(data.get("message"))
return data
res = align_lyrics(song_id="17e52af7-b5df-4877-a98a-dce94e0e6aa2", vocal_separate=True)
print("качество выравнивания coverage =", res["coverage"])
for line in res["lines"]:
print(f'{line["start"]:7.2f} {line["text"]}')
# караоке по символам: у каждого символа в line["chars"] свои start / end
Частые вопросы
В: Вывод по символам или по строкам?
О: И то и другое. lines[].start — по строкам, lines[].chars[].start / end — по символам; плеер берёт, что нужно.
В: Почему возвращённый текст совпадает с отправленным? О: Так задумано. Мы не распознаём и не переписываем, а только выравниваем время, поэтому текст никогда не «исправится в опечатку».
В: coverage высокий, но всё равно звучит немного смещённо.
О: Ошибка по символам обычно в пределах десятков миллисекунд; если вся песня смещена на постоянную величину, скорее всего, в начале аудио есть тишина — обрежьте её и попробуйте снова.
В: Почему только что разделение работало, а теперь «вокальная дорожка недоступна»? О: Результаты разделения — кратковременные адреса (по измерениям, истекают примерно через полчаса). Тогда эндпоинт пытается разделить заново; если и это не удалось — полный возврат. Поэтому «разделить сейчас, выровнять потом» ненадёжно: делайте оба шага в одном процессе.
В: Нужно ли самому разделять песню платформы?
О: Нет. При vocal_separate: true мы проверяем, есть ли у песни вокальная дорожка; если пригодной нет, вернём ошибку и деньги, а не подсунем полный микс.
В: Можно пакетно? О: Одна песня за запрос. Для пакета делайте цикл; каждый вызов тарифицируется, поэтому результаты лучше кэшировать.
В: Результат сохраняется? О: Этот эндпоинт ничего не сохраняет; сохраняйте возвращённое значение сами (таймкоды по строкам и символам есть в ответе).
Получить API-ключ
Создайте API-ключ в консоли после регистрации. Одна генерация стоит 0,6 ¥ и возвращает 2 трека, загрузки не ограничены, номер телефона не нужен.
Получить API-ключ Вернуться к обзору