Lyrics timestamp alignment API

Updated 2026-09-18 · Suno-API team

This is not speech recognition. The text always uses the lyrics you supply verbatim; we only place each character at the correct time — so you never get "misheard characters making the lyrics mismatch".

Endpoint description

It aligns existing lyrics with audio and outputs line-level + character-level timestamps, for karaoke character highlighting, scrolling lyrics and subtitle generation.

Endpoint: POST /api/music/lyrics-align

Authentication: see Authentication; the request header is Authorization: Bearer YOUR_API_KEY

Billing: charged per call (currently 0.1 ¥ per call by default); for a platform song with vocal separation selected, the platform's "vocal/backing separation" price is charged once more. The amount actually charged follows the current back-office price configuration.

Average time: 5–15 seconds (depending on audio length and whether vocal separation has to be done on the spot)

Why calibration is needed: generated timestamps are not always accurate

The timestamped lyrics API returns the generation side's per-word timestamps directly, and they carry inaccuracies:

Lyrics timestamp alignment re-does the time alignment on the lyrics you supply yourself: only the timing is corrected, never the text, which suits cases that care about "does it line up" (KTV character highlighting, per-character subtitles, sing-along scoring).

Recommended approach (three steps, best results)

  1. Extract the vocal first (vocal/backing separation) and align only the vocal track — backing and harmonies stop interfering. Measured basis: in a line-by-line comparison of "full mix vs vocal track" for the same song, 5 of 28 lines differed by more than 0.25 s (the largest by 2.64 s); two independent engines differed on average by 0.239 s on the vocal track and 0.508 s on the full mix — nearly half the agreement. So for character highlighting (KTV/subtitles) it is recommended to switch it on. (The exact gain is being re-measured with the new engine.)
  2. Provide the complete lyrics: the whole song, line by line, matching the audio one-to-one. A missing sentence or section shifts all the following lines; do not include timestamps such as [00:23.82] in the lyrics (they are ignored automatically).
  3. For a platform song you can simply tick vocal_separate: true, and we do "separation + alignment" in one step.

Three ways to use it

Usage What you send Where the lyrics come from Vocal separation
External song · public URL audio_url + lyrics You supply them ❌ Not supported
External song · direct local upload (new in 2026-10) audio_object_key + lyrics You supply them ❌ Not supported
Platform song (generated on this site) song_id We take them from the song record ✅ Optional

How the parameters relate (the easiest thing to get wrong — read carefully)

New: direct local upload (the audio never passes through our servers)

Use this when the audio is on your machine and you would rather not upload it to your own public storage first. Two steps:

  1. POST /api/music/lyrics-align/upload-ticket with file_name / file_size / content_type and get back a put_url (a signed URL with no long-lived credentials) and an object_key;
  2. PUT the file to put_url, carrying the content_type from the response exactly as given (it takes part in the signature; one changed character means 403);
  3. When submitting the alignment, send audio_object_key instead of audio_url.

The credential is single-use: once the alignment finishes (success or failure) the server deletes the object, and reusing the same key reports an error.

New: the lyrics are now optional

External songs used to require lyrics. Now you can omit them — the server runs recognition once and uses the recognised text as the lyrics.

The trade-off must be stated clearly: the text then follows the recognition result, so individual characters may be wrong. Such responses are clearly marked with lyrics_source: "asr", confidence: "low" and, in warnings[], {"code":"no_reference_lyrics"}.

For character-level highlighting / KTV, always supply the original lyrics.

Request parameters

Headers

Parameter Type Required Notes
Authorization string yes Bearer YOUR_API_KEY
Content-Type string yes application/json

Body parameters

Parameter Type Required Default Notes
song_id string one of two - Platform song ID (platform song route)
lyrics string no - The original lyrics, lines separated by \n. Can now be omitted — see "the lyrics are now optional" above
audio_url string one of two for external - Audio address; must be publicly downloadable
audio_object_key string one of two for external - Key of the direct-upload object (call /lyrics-align/upload-ticket first)
vocal_separate boolean no false Whether to separate vocals first (platform songs only)
language string no auto-detected Chinese / English etc.; when omitted it is guessed from the lyrics
granularity string no both both / line (no per-character output) / word
sections string no keep keep retains section tags such as [Chorus] (they do not take part in the timeline) / drop
format string no json json / lrc / lrc-word (the latter two also return the lrc and lrc_word text)

Limits

Item Limit
Audio length ≤ 6 minutes (longer returns an error and a full refund)
Audio source Must be publicly downloadable; addresses that need a login are not accepted
Lyrics format Plain text, lines separated by \n; section tags such as [Verse] and 【主歌】 are kept as-is but do not take part in the timeline
Batching One song per call; loop over multiple songs yourself
Result storage This endpoint does not persist anything; save the returned result yourself

Request examples

External song (lyrics + audio address)

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": "Wandering the chilly streets\nNight wind brushes my eyes\nTime flows on like water",
    "audio_url": "https://your-bucket.example.com/song.mp3"
  }'

Platform song (no separation)

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"}'

Platform song + vocal separation (recommended for karaoke)

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}'

Response

Success response

{
  "success": true,
  "lyrics_source": "platform",
  "vocal_separate": false,
  "coverage": 0.935,
  "exact": 196,
  "fuzzy": 4,
  "total": 214,
  "lines": [
    {
      "text": "Wandering the chilly streets",
      "start": 21.66,
      "end": 24.06,
      "chars": [
        {"char": "W", "start": 21.66, "end": 21.74, "word": true},
        {"char": "a", "start": 21.98, "end": 22.22, "word": true}
      ]
    }
  ]
}

Field reference

Field Type Notes
success boolean Whether the request succeeded
lyrics_source string platform = lyrics taken from the platform song; user = lyrics supplied by you; asr = you supplied none and the text came from recognition
vocal_separate boolean Whether vocal separation was used this time
confidence string high / low (always low when lyrics_source=asr)
warnings[] array Degradation notices; contains {"code":"no_reference_lyrics"} when there were no lyrics
coverage number Alignment quality: share of characters with a clear time anchor (0–1)
exact / fuzzy / total number Exact-match characters / homophone-match characters / valid lyric characters
lines[].text string One lyric line, character for character equal to your original
lines[].start / end number Start and end time of the line (seconds)
lines[].section boolean Whether the line is a section tag (true means no time and no highlighting)
lines[].chars[] array Per-character times: char, start/end in seconds, word whether it counts towards the timeline (punctuation is false)
words[] array Flat per-character list: text / start / end / line; not returned when granularity=line
lrc string Line-level LRC text (only with format=lrc or lrc-word)
lrc_word string Enhanced per-character LRC with inline <mm:ss.xx> tags (only with format=lrc-word)

How to use coverage (three tiers)

coverage Suggested use
≥ 0.9 Character highlighting (karaoke) is fine
0.8 – 0.9 Mix per-character and whole-line, or highlight whole lines only and treat per-character as indicative
< 0.8 Whole-line highlighting only (reprises, heavy harmonies, dialects and rap tend to score low)

Status and timing

Scenario Measured time
Platform song · no separation about 4.5 s
Platform song · separation (vocal track already present) about 11 s
Platform song · separation (must be separated on the spot) about 25 s, up to about 1 minute
External song · lyrics + audio address about 5–15 s (depending on audio length)

The server's single-run limit is 8 minutes; set your client timeout to 120 seconds or more.

Error responses

{
  "success": false,
  "message": "音频超过 6 分钟上限(实际 7.2 分钟)"
}
Scenario Charged?
No audio at all (none of the three sent) Not charged (processing never started)
audio_url and audio_object_key sent together Not charged (processing never started)
The direct-upload object is not on disk yet / has been deleted Not charged (processing never started)
Invalid granularity / sections / format Not charged (processing never started)
The platform song does not exist / is not on the current account Not charged (processing never started)
Missing or invalid parameters Not charged (processing never started)
Audio longer than the 6-minute limit Full refund
Audio not downloadable (not a public address / needs a login) Full refund
No lyrics supplied and server-side recognition failed Full refund
Vocal separation ticked but no usable vocal track Full refund (including the separation fee)
Alignment service error or timeout Full refund

Principle: no final result means a full refund. The charge applies only when per-character timestamps are actually returned, and refunds are automatic — no request needed.

Billing

Item When it is charged Notes
Lyrics timestamp alignment (base) Every call Charged per call (currently 0.1 ¥ per call by default); the amount actually charged follows the back-office price configuration
Transcription surcharge Only when no lyrics are supplied The server must run speech recognition first, a cost the "with lyrics" tier does not have, so it is priced separately
Vocal separation (optional, platform songs only) When vocal_separate is ticked Charged once at the platform's existing "vocal/backing separation" price and settled together with this endpoint

The combination:

amount due = base price + (no lyrics supplied ? transcription surcharge : 0) + (separation ticked ? separation price : 0)

The vocal separation fee is charged only once: if you have already separated the song and the vocal track is available directly, this endpoint charges it; if separation has to happen on the spot, the separation step charges it.

Full example (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("alignment quality coverage =", res["coverage"])
for line in res["lines"]:
    print(f'{line["start"]:7.2f}  {line["text"]}')
    # per-character karaoke: every character in line["chars"] has its own start / end

FAQ

Q: Is the output per character or per line? A: Both. lines[].start is line-level; lines[].chars[].start / end are per character — the player takes whichever it needs.

Q: Why is the returned text identical to what I sent? A: By design. We do not recognise-and-rewrite; we only align timing, so the lyrics can never be "corrected into typos".

Q: coverage is high but it still sounds slightly off? A: Per-character alignment error is usually in the tens of milliseconds; if the whole song is off by a fixed amount, it is most likely a stretch of silence at the start of the audio, so trim it and try again.

Q: Why did separation work a moment ago, and now it reports "vocal track unavailable"? A: Separation results are short-lived temporary addresses (measured to expire after roughly half an hour). In that case the endpoint tries to separate again automatically; if it still fails you get a full refund. So "separate now, align later" is not reliable — do the two in the same flow.

Q: Do I need to separate a platform song myself first? A: No. With vocal_separate: true we check whether the song already has a vocal track; if there is no usable vocal track it reports an error and refunds rather than fobbing you off with the full mix.

Q: Can I batch them? A: One song per request. For batches, loop; every call is charged normally, so caching results locally is recommended.

Q: Is the result saved? A: This endpoint does not persist anything; save the returned value yourself (both line-level and per-character timestamps are in the response).

NextMusic commercial licensing certificate API

Get an API key

Create an API key in the console after signing up. ¥0.6 per run returns 2 tracks, downloads are unlimited, and no phone number is required.

Get API key Back to overview