ListenHubDocs
API Reference

Lyrics Generation

Turn a short prompt into song lyrics with Mureka or Suno, then read the finished variants from the task.

The Lyrics API turns a short prompt into complete song lyrics. It runs on the same two providers as the Music API — Mureka and Suno — chosen per request with the provider field. All endpoints live under https://api.marswave.ai/openapi/v1/lyrics and authenticate with Authorization: Bearer $LISTENHUB_API_KEY.

Generation is always a task. POST /v1/lyrics/generate returns a taskId and a status, never the lyrics themselves; read the finished text from GET /v1/lyrics/tasks/{taskId}.

Every response is wrapped in { "code": 0, "message": "", "data": { ... } }. A non-zero code means an error — see Error Handling. The examples below read fields from data.

Providers

provider takes mureka, suno, or default, which resolves to Mureka. The choice does not change the request shape — only how quickly the task reaches a terminal state, and how many variants come back.

ProviderHow it runsstatus in the create responseVariants
murekaSynchronous. The create request waits for the upstream result.success — already terminalOne
sunoCallback-based. The provider posts the result back when it finishes.pendingTypically two
defaultResolves to the service default, which is Mureka.Same as murekaOne

Omitting provider is the same as sending default. The task always reports the provider that actually ran it, so a task created with default reads back as mureka.

Task Lifecycle

status moves through pending → generating → success or failed. Both providers share that state machine and differ only in how long a task stays short of a terminal state.

  1. POST /v1/lyrics/generate returns 202 with a taskId and the initial status.
  2. On Mureka that status is already success — fetch the task once to read the lyrics. On Suno it is pending — poll GET /v1/lyrics/tasks/{taskId} every few seconds.
  3. On success, read variants. On failed, read errorMessage.

Suno exposes no query endpoint for lyrics, so its callback is the only path to success. A Suno task that goes 30 minutes without an update is marked failed and its credits are refunded in full; it will not recover after that.

Generate Lyrics

POST /v1/lyrics/generate

Start a lyrics task from a prompt. Sends JSON.

curl -X POST "https://api.marswave.ai/openapi/v1/lyrics/generate" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A hopeful anthem about leaving a small town at dawn",
    "provider": "suno"
  }'
const response = await fetch(
  'https://api.marswave.ai/openapi/v1/lyrics/generate',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      prompt: 'A hopeful anthem about leaving a small town at dawn',
      provider: 'suno',
    }),
  }
);
const { data } = await response.json();
console.log('Task:', data.taskId, data.status);
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/lyrics/generate',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'prompt': 'A hopeful anthem about leaving a small town at dawn',
        'provider': 'suno',
    },
)
data = response.json()['data']
print('Task:', data['taskId'], data['status'])

Request parameters:

FieldTypeRequiredDescription
promptstringYesWhat the lyrics should be about. Maximum 200 characters — a longer prompt is rejected during validation, before any credits are reserved
providerstringNomureka, suno, or default. Defaults to default, which resolves to Mureka

Response example:

{
  "code": 0,
  "message": "",
  "data": {
    "taskId": "68e780390fc5c9a54f695a7e",
    "status": "pending"
  }
}

The create response carries only taskId and status on both providers — the lyrics are never in it, not even on Mureka, where the task is already success. Fetch the task to read variants.

This endpoint counts against the creation request limit described under Rate Limits.

List Tasks

GET /v1/lyrics/tasks

List your lyrics tasks, newest first.

curl -X GET "https://api.marswave.ai/openapi/v1/lyrics/tasks?page=1&pageSize=20&status=success" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"
const response = await fetch(
  'https://api.marswave.ai/openapi/v1/lyrics/tasks?page=1&pageSize=20',
  { headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` } }
);
const { data } = await response.json();
console.log(`${data.items.length} of ${data.total} tasks`);
import os
import requests

response = requests.get(
    'https://api.marswave.ai/openapi/v1/lyrics/tasks',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    params={'page': 1, 'pageSize': 20},
)
data = response.json()['data']
print(len(data['items']), 'of', data['total'], 'tasks')

Query parameters:

FieldTypeRequiredDescription
pageintegerNoPage number, min 1. Defaults to 1
pageSizeintegerNoItems per page, 1–100. Defaults to 20
statusstringNoFilter by pending, generating, success, or failed

data holds the page, not a bare array: items carries the tasks in the shape described under Get a Task, alongside page, pageSize, and total.

Get a Task

GET /v1/lyrics/tasks/{taskId}

Fetch a single task. This is the endpoint you poll after submitting to Suno, and the one that returns the lyrics on either provider.

curl -X GET "https://api.marswave.ai/openapi/v1/lyrics/tasks/{taskId}" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"
const response = await fetch(
  `https://api.marswave.ai/openapi/v1/lyrics/tasks/${taskId}`,
  { headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` } }
);
const { data } = await response.json();
console.log('Status:', data.status);
if (data.status === 'success') console.log(data.variants[0].text);
import os
import requests

response = requests.get(
    f'https://api.marswave.ai/openapi/v1/lyrics/tasks/{task_id}',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
)
data = response.json()['data']
print('Status:', data['status'])
if data['status'] == 'success':
    print(data['variants'][0]['text'])

Response example:

{
  "code": 0,
  "message": "",
  "data": {
    "id": "68e780390fc5c9a54f695a7e",
    "provider": "suno",
    "status": "success",
    "params": {
      "prompt": "A hopeful anthem about leaving a small town at dawn"
    },
    "variants": [
      {
        "title": "Dawn Highway",
        "text": "Suitcase on the porch light\nEngine turning over slow\n...",
        "status": "complete",
        "errorMessage": ""
      },
      {
        "title": "First Light Out",
        "text": "The bus stop hums awake\nI count the streetlamps one last time\n...",
        "status": "complete",
        "errorMessage": ""
      }
    ],
    "creditCost": 2,
    "errorMessage": "",
    "createdAt": 1730000000000,
    "updatedAt": 1730000021000
  }
}

Task response fields:

FieldTypeDescription
idstringTask ID
providerstringThe provider that ran the task: mureka or suno
statusstringpending, generating, success, failed
params.promptstringEcho of the submitted prompt
variantsarrayGenerated lyrics. Empty until the task succeeds
variants[].titlestringSuggested song title
variants[].textstringThe generated lyrics
variants[].statusstringcomplete or failed. A successful task can still carry a failed variant
variants[].errorMessagestringWhy this variant failed; empty otherwise
creditCostnumberCredits charged. Stays 0 until the task succeeds
errorMessagestringFailure reason (only when status is failed)
createdAtnumberCreation time (ms timestamp)
updatedAtnumberLast update time (ms timestamp)

Tasks are owner-scoped: fetching a task that belongs to another account returns an error rather than the task.

Credits

Lyrics generation costs a flat 2 credits per request — the same on both providers, and independent of how many variants come back.

Credits are reserved when the task is created and confirmed when it succeeds. They are refunded in full when the provider rejects the request, when the task fails, and when a Suno task times out. A task that never reaches success therefore costs nothing, and its creditCost stays 0.

Check your live balance with GET /v1/user/subscription, and see Pricing for the full credit reference.

SDK and CLI

The Lyrics API is HTTP-only on the API-key surface: neither the OpenAPIClient SDK nor the listenhub openapi CLI wraps these endpoints yet. The listenhub lyrics commands and the ListenHubClient lyrics methods call the same endpoints, but authenticate with an account login rather than an API key.

On this page