Skip to main content
Synced lyrics provide a karaoke-style experience by displaying lyrics at precise timestamps during playback. This guide shows you how to create, sync, and manage lyrics for your tracks.

Prerequisites

  • An authenticated user account (see Authentication)
  • A valid JWT token
  • A track ID from a published release
  • The audio file for accurate timing
  • Plain text lyrics

Understanding synced lyrics

Synced lyrics in Lens Music consist of:
  • content: Array of lyric lines with optional timestamps
  • trackId: UUID of the associated track
  • language: Language code (defaults to “en”)
Each line can have a timestamp indicating when it should appear during playback.

Lyrics data structure

Lyrics are stored as a JSON array with this format:
{
  "content": [
    { "time": "0.00", "text": "First line of lyrics" },
    { "time": "3.50", "text": "Second line of lyrics" },
    { "text": "Line without timestamp" }
  ],
  "trackId": "track-uuid",
  "language": "en"
}
The time field is optional. Lines without timestamps will still be stored but won’t sync to playback.

Creating unsynced lyrics

Start by creating lyrics without timestamps, which you can sync later.
1

Prepare lyrics content

Format your lyrics as an array of text objects:
const lyricsContent = [
  { "text": "Walking down the midnight street" },
  { "text": "City lights beneath my feet" },
  { "text": "Dreams are calling out my name" },
  { "text": "Nothing's ever quite the same" }
];
2

Send create lyrics request

curl -X POST https://api.lensmusic.com/lyrics \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": [
      {"text": "Walking down the midnight street"},
      {"text": "City lights beneath my feet"}
    ],
    "trackId": "track-uuid",
    "language": "en"
  }'
3

Handle the response

{
  "message": "Lyrics created successfully",
  "data": {
    "id": "lyrics-uuid",
    "content": [
      { "text": "Walking down the midnight street" },
      { "text": "City lights beneath my feet" },
      { "text": "Dreams are calling out my name" },
      { "text": "Nothing's ever quite the same" }
    ],
    "trackId": "track-uuid",
    "language": "en",
    "createdAt": "2026-03-04T10:30:00Z",
    "updatedAt": "2026-03-04T10:30:00Z"
  }
}

Syncing lyrics with timestamps

Add precise timestamps to create synchronized lyrics.

Manual timestamp method

Create lyrics with pre-calculated timestamps:
const syncedLyrics = {
  content: [
    { time: "0.00", text: "Walking down the midnight street" },
    { time: "3.50", text: "City lights beneath my feet" },
    { time: "7.20", text: "Dreams are calling out my name" },
    { time: "11.00", text: "Nothing's ever quite the same" }
  ],
  trackId: 'track-uuid',
  language: 'en'
};

const response = await fetch('https://api.lensmusic.com/lyrics', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(syncedLyrics)
});

Interactive sync workflow

The Lens Music client provides an interactive syncing interface. Here’s how it works:
1

Upload audio file

Load the track’s audio file into the sync interface:
const handleFileUpload = (event) => {
  const file = event.target.files[0];
  const url = URL.createObjectURL(file);
  setAudio(url);
};
2

Navigate through lyrics

Use keyboard shortcuts to move through lyrics:
  • Arrow Up: Move to previous line
  • Arrow Down: Move to next line
  • Space: Sync current line to playback time
const handleKeyDown = (event) => {
  if (event.code === 'ArrowUp' && currentLineIndex > 0) {
    setCurrentLineIndex(prev => prev - 1);
  } else if (event.code === 'ArrowDown') {
    setCurrentLineIndex(prev => prev + 1);
  } else if (event.code === 'Space' && isPlaying) {
    event.preventDefault();
    handleSync(currentLine, currentLineIndex);
  }
};
3

Record timestamps

As the audio plays, press Space when each line should appear:
const handleSync = (line, index) => {
  setSyncedLyrics(prev => {
    const newSyncedLyrics = prev.filter(item => item.index !== index);
    return [...newSyncedLyrics, {
      time: currentTime,
      line,
      index
    }].sort((a, b) => a.index - b.index);
  });
};
4

Save synced lyrics

Once all timestamps are recorded, format and save:
const formattedLyrics = syncedLyrics.map(item => ({
  time: item.time.toFixed(2),
  text: item.line
}));

const response = await fetch(`https://api.lensmusic.com/lyrics/${lyricsId}`, {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    content: formattedLyrics
  })
});

Fetching lyrics

Retrieve lyrics for tracks or specific lyrics by ID.

List lyrics for a track

curl -X GET "https://api.lensmusic.com/lyrics?trackId=track-uuid" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Get specific lyrics

curl -X GET https://api.lensmusic.com/lyrics/{lyricsId} \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
Response:
{
  "message": "Lyrics fetched successfully",
  "data": {
    "id": "lyrics-uuid",
    "content": [
      { "time": "0.00", "text": "Walking down the midnight street" },
      { "time": "3.50", "text": "City lights beneath my feet" },
      { "time": "7.20", "text": "Dreams are calling out my name" },
      { "time": "11.00", "text": "Nothing's ever quite the same" }
    ],
    "trackId": "track-uuid",
    "language": "en",
    "track": {
      "id": "track-uuid",
      "title": "Midnight Dreams",
      "duration": 240
    }
  }
}

Language support

Set the lyrics language using ISO 639-1 language codes:
{
  "language": "en",  // English
  "language": "es",  // Spanish
  "language": "fr",  // French
  "language": "de",  // German
  "language": "ja",  // Japanese
  "language": "ko"   // Korean
}
The language field defaults to “en” (English) if not specified.

Timestamp format

Timestamps should be formatted as strings representing seconds with two decimal places:
// Good formats
"0.00"    // Start
"3.50"    // 3.5 seconds
"127.45"  // 2 minutes 7.45 seconds

// Bad formats
3.5       // Number instead of string
"3:30"    // Minutes:seconds format
"3500"    // Milliseconds

Pagination parameters

ParameterTypeDefaultDescription
pageinteger0Zero-based page number
sizeinteger10Number of lyrics per page
trackIdstring-Filter by track UUID

Best practices

Accurate timing

Test synced lyrics with the actual audio to ensure timestamps are accurate and lyrics appear at the right moment.

Line breaks

Keep lyrics lines to a readable length. Split long phrases into multiple lines for better display.

Language tags

Always specify the correct language code for proper text rendering and language-specific features.

Empty lines

Use empty text entries with timestamps for instrumental breaks:
{ "time": "45.00", "text": "" }

Playback implementation

To display synced lyrics during playback:
const getCurrentLyricLine = (currentTime, lyrics) => {
  // Find the lyric line for current playback time
  let currentLine = null;
  
  for (let i = 0; i < lyrics.content.length; i++) {
    const line = lyrics.content[i];
    const nextLine = lyrics.content[i + 1];
    
    const lineTime = parseFloat(line.time || 0);
    const nextTime = nextLine ? parseFloat(nextLine.time || Infinity) : Infinity;
    
    if (currentTime >= lineTime && currentTime < nextTime) {
      currentLine = line;
      break;
    }
  }
  
  return currentLine;
};

// Usage in audio player
audioElement.addEventListener('timeupdate', () => {
  const currentLine = getCurrentLyricLine(audioElement.currentTime, lyrics);
  displayLyric(currentLine?.text || '');
});

Error handling

{
  "message": "Track not found"
}
The specified track ID doesn’t exist. Verify the track was created successfully.
{
  "message": "Validation failed",
  "errors": [
    "content must be an array"
  ]
}
Ensure content is an array of objects with text and optional time fields.
{
  "message": "Invalid language code"
}
Use valid ISO 639-1 language codes (e.g., “en”, “es”, “fr”).

Next steps

API Reference

View complete lyrics API documentation

Distributing releases

Learn more about managing releases

Build docs developers (and LLMs) love