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.
| Provider | How it runs | status in the create response | Variants |
|---|---|---|---|
mureka | Synchronous. The create request waits for the upstream result. | success — already terminal | One |
suno | Callback-based. The provider posts the result back when it finishes. | pending | Typically two |
default | Resolves to the service default, which is Mureka. | Same as mureka | One |
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.
POST /v1/lyrics/generatereturns202with ataskIdand the initialstatus.- On Mureka that status is already
success— fetch the task once to read the lyrics. On Suno it ispending— pollGET /v1/lyrics/tasks/{taskId}every few seconds. - On
success, readvariants. Onfailed, readerrorMessage.
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:
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | What the lyrics should be about. Maximum 200 characters — a longer prompt is rejected during validation, before any credits are reserved |
provider | string | No | mureka, 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:
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number, min 1. Defaults to 1 |
pageSize | integer | No | Items per page, 1–100. Defaults to 20 |
status | string | No | Filter 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:
| Field | Type | Description |
|---|---|---|
id | string | Task ID |
provider | string | The provider that ran the task: mureka or suno |
status | string | pending, generating, success, failed |
params.prompt | string | Echo of the submitted prompt |
variants | array | Generated lyrics. Empty until the task succeeds |
variants[].title | string | Suggested song title |
variants[].text | string | The generated lyrics |
variants[].status | string | complete or failed. A successful task can still carry a failed variant |
variants[].errorMessage | string | Why this variant failed; empty otherwise |
creditCost | number | Credits charged. Stays 0 until the task succeeds |
errorMessage | string | Failure reason (only when status is failed) |
createdAt | number | Creation time (ms timestamp) |
updatedAt | number | Last 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.
Music Generation
Generate songs, instrumentals, and soundtracks with Mureka or Suno, and analyze existing audio — lyrics recognition, description, and stem separation.
Voice Cloning
Upload reference audio to create a reusable private voice, confirm it, and use its speaker ID with the speech and TTS endpoints.