ListenHubOpenAPI
API Reference

Seed Audio

Turn text, reference voices, or an image into speech and sound effects with end-to-end async generation, then poll the task for the finished audio.

The Seed Audio API generates audio end to end — plain narration, sound effects, single-voice speech, multi-speaker dialogue, voice cloning from a reference clip, or image-to-audio. Generation is asynchronous: you submit a request and poll a task until it finishes. All endpoints live under https://api.marswave.ai/openapi/v1/seed-audio and authenticate with Authorization: Bearer $LISTENHUB_API_KEY.

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.

Model and Limits

ItemValue
modelseed-audio-1.0 (default, the only supported value)
Rate limit5 requests per minute, per user, on /generate
textUp to 1400 characters
voices1–3 entries (omit for plain text / sound effects)
durationHint1–110 seconds (credit estimate + a hint for the target length)

Voices

voices controls who speaks. Each entry is one of two kinds:

typeRequired fieldDescription
speakeridA built-in voice — a ListenHub voice code or a Doubao voice_type. Do not send url for this type.
referenceurlA custom reference audio URL (http/https) to clone the voice from. Up to 30s, ≤10MB, wav/mp3/pcm/ogg_opus. Do not send id for this type.

For multi-speaker dialogue, list 2–3 voices and prefix each line of text with @音频1, @音频2, … to assign lines to voices in order. Omit voices entirely to generate plain narration or pure sound effects.

voices and image are mutually exclusive — send at most one. A request that includes both is rejected. A speaker entry must carry only id; a reference entry must carry only url. Mixing them returns 33004 (invalid params).

Async Task Lifecycle

  1. Submit a generation request to POST /v1/seed-audio/generate. The response carries a taskId and an initial status of pending.
  2. Poll GET /v1/seed-audio/tasks/{taskId}. status moves through pendinggeneratinguploadingsuccess.
  3. On success, read audioUrl. On failed, read errorMessage.
StatusMeaning
pendingCreated, waiting to be submitted for generation.
generatingGeneration in progress. audioUrl is not yet available.
uploadingGeneration finished, transferring the audio to storage.
successDone. audioUrl is available.
failedFailed at some stage. errorMessage explains why; any reserved credits are refunded.

Create a Seed-Audio Task

POST /v1/seed-audio/generate

Submit text (optionally with voices or a reference image) for end-to-end audio generation. Sends JSON. Returns 202 with a taskId.

# Plain text / sound effects (no voices)
curl -X POST "https://api.marswave.ai/openapi/v1/seed-audio/generate" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "A gentle rain falls on a quiet street at midnight.",
    "durationHint": 20
  }'

# Single voice (speaker)
curl -X POST "https://api.marswave.ai/openapi/v1/seed-audio/generate" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Welcome to ListenHub. Here is your daily briefing.",
    "voices": [{ "type": "speaker", "id": "zh_female_warm" }]
  }'

# Voice cloning from a reference clip
curl -X POST "https://api.marswave.ai/openapi/v1/seed-audio/generate" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "@音频1 Hi there! @音频2 Hello, how can I help?",
    "voices": [
      { "type": "reference", "url": "https://example.com/host.mp3" },
      { "type": "speaker", "id": "zh_male_calm" }
    ]
  }'
const response = await fetch(
  'https://api.marswave.ai/openapi/v1/seed-audio/generate',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.LISTENHUB_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      text: 'Welcome to ListenHub. Here is your daily briefing.',
      voices: [{ type: 'speaker', id: 'zh_female_warm' }],
      durationHint: 20,
    }),
  },
)
const { data } = await response.json()
console.log('Task ID:', data.taskId)
import os
import requests

response = requests.post(
    'https://api.marswave.ai/openapi/v1/seed-audio/generate',
    headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
    json={
        'text': 'Welcome to ListenHub. Here is your daily briefing.',
        'voices': [{'type': 'speaker', 'id': 'zh_female_warm'}],
        'durationHint': 20,
    },
)
data = response.json()['data']
print('Task ID:', data['taskId'])

For image-to-audio, send an image object instead of voices (the two are mutually exclusive):

{
  "text": "Describe this scene as a short narrated clip.",
  "image": { "url": "https://example.com/scene.jpg" }
}

Request parameters:

FieldTypeRequiredDescription
modelstringNoseed-audio-1.0. Defaults to seed-audio-1.0
textstringYesScript to speak. Up to 1400 characters. Use @音频N prefixes to assign dialogue lines to voices
voicesarrayNo1–3 voice entries (see Voices). Omit for plain text / sound effects. Mutually exclusive with image
imageobjectNoReference image for image-to-audio. Provide exactly one of url (http/https) or data (Base64, optionally with a data:image/...;base64, prefix). One image, ≤10MB, jpeg/png/webp. Mutually exclusive with voices
audioConfigobjectNoOutput tuning (see below)
durationHintnumberNoTarget length, 1110 seconds. Drives the credit estimate and hints the model
watermarkbooleanNoAdd an audio watermark

audioConfig fields:

FieldTypeRequiredDescription
speechRatenumberNoSpeaking rate, -50100
loudnessRatenumberNoLoudness, -50100
pitchRatenumberNoPitch, -1212
formatstringNomp3 (default), wav, pcm, or ogg_opus

Returns 202:

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

Get a Task

GET /v1/seed-audio/tasks/{taskId}

Fetch a single task. This is the endpoint you poll after submitting a generation request.

curl -X GET "https://api.marswave.ai/openapi/v1/seed-audio/tasks/{taskId}" \
  -H "Authorization: Bearer $LISTENHUB_API_KEY"
const response = await fetch(
  `https://api.marswave.ai/openapi/v1/seed-audio/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('Audio:', data.audioUrl)
import os
import requests

response = requests.get(
    f'https://api.marswave.ai/openapi/v1/seed-audio/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('Audio:', data['audioUrl'])

A successful task:

{
  "code": 0,
  "message": "",
  "data": {
    "id": "68e780390fc5c9a54f695a7e",
    "status": "success",
    "model": "seed-audio-1.0",
    "params": {
      "text": "Welcome to ListenHub. Here is your daily briefing.",
      "voices": [{ "type": "speaker", "id": "zh_female_warm" }]
    },
    "audioUrl": "https://assets.listenhub.ai/seed-audio/68e780390fc5c9a54f695a7e.mp3",
    "audioDuration": 18.4,
    "creditCharged": 12,
    "creditRefunded": 0,
    "createdAt": 1730000000000,
    "updatedAt": 1730000040000
  }
}

Task response fields:

FieldTypeDescription
idstringTask ID
statusstringpending, generating, uploading, success, or failed
modelstringseed-audio-1.0
paramsobjectEcho of the submitted request (sensitive image/audio payloads are stripped; an inline image shows as { "hasData": true } with an optional thumbnailUrl)
audioUrlstringFinished audio URL. Returned only when status is success
audioDurationnumberAudio length in seconds (the billed duration)
creditChargednumberCredits actually charged (0 if not yet charged)
creditRefundednumberCredits refunded on failure (for reconciliation)
errorMessagestringFailure reason. Returned only when status is failed
createdAtnumberCreation time (ms timestamp)
updatedAtnumberLast update time (ms timestamp)

List Tasks

GET /v1/seed-audio/tasks

List your Seed Audio tasks, newest first.

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

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

Query parameters:

FieldTypeRequiredDescription
pageintegerNoPage number, min 1. Defaults to 1
pageSizeintegerNoItems per page, 1100. Defaults to 20
statusstringNoFilter by pending, generating, uploading, success, or failed
keywordstringNoFuzzy match against the task text. Up to 64 characters

Response:

{
  "code": 0,
  "message": "",
  "data": {
    "items": [
      {
        "id": "68e780390fc5c9a54f695a7e",
        "status": "success",
        "model": "seed-audio-1.0",
        "audioUrl": "https://assets.listenhub.ai/seed-audio/68e780390fc5c9a54f695a7e.mp3",
        "audioDuration": 18.4,
        "creditCharged": 12,
        "creditRefunded": 0,
        "createdAt": 1730000000000,
        "updatedAt": 1730000040000
      }
    ],
    "page": 1,
    "pageSize": 20,
    "total": 1
  }
}

Each item carries the same fields as Get a Task.

Errors

Business errors return HTTP 400 with the specific code in the top-level code field.

CodeMeaning
33001Task not found (or not owned by the current API user)
33002Speaker not found for a speaker voice entry
33003Generation service unavailable
33004Invalid parameters (e.g. voices and image sent together, or a voice entry mixing id and url)
33005Too many voices (max 3)
33006Not enough credits
33007Rate limited
33008Generation timed out
33009Per-user concurrency limit reached
HTTP statusMeaning
400Invalid parameters or a business error — see the 33xxx codes above
429Rate limit exceeded (5 RPM per user on /generate)

Credits

Credits are reserved at submission, confirmed on success, and refunded automatically on failure. Each task reports creditCharged (actually charged) and creditRefunded (refunded on failure) for reconciliation. The billed length is audioDuration. Check your live balance with GET /v1/user/subscription, and see Pricing for credit-to-feature mapping.

On this page