
This guide shows you how to generate a full song — vocals, instruments, and structure — from a style prompt and lyrics using minimax-music-1.5 on hiapi's unified task API. Everything below was verified against the live endpoint, so you can copy, paste, and run it.
The goal: submit a text description of a musical style plus your lyrics, and get back a downloadable audio track.
You need exactly one thing: a hiapi API key. Grab it from your dashboard — it starts with sk-. All examples below assume it's in an environment variable:
export HIAPI_KEY="sk-your-key-here"
Music generation on hiapi runs through the same async task interface as every other model: POST /v1/tasks to create a job, then either poll GET /v1/tasks/<taskId> or receive a callback when it finishes.
minimax-music-1.5 accepts these fields inside input — and rejects anything else with a 400:
| Field | Required | Type | Constraints |
|---|---|---|---|
prompt | yes | string | 10–300 characters. Describes genre, mood, instrumentation, vocal style. |
lyrics | yes | string | 10–600 characters. The words to sing; supports section tags like [Verse] and [Chorus]. |
sample_rate | no | integer | One of 16000, 24000, 32000, 44100. |
bitrate | no | integer | One of 32000, 64000, 128000, 256000. |
Two things trip people up:
prompt and lyrics are required. The prompt controls how it sounds; the lyrics control what is sung. Omit either and you get invalid input: ... missing required field.duration, seed, format, instrumental — are not accepted. Sending them returns additional properties ... not allowed.Create the task:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax-music-1.5",
"input": {
"prompt": "Uplifting acoustic pop, warm female vocals, gentle guitar and light percussion",
"lyrics": "[Verse]\nMorning light on a quiet street\nCoffee steam and a steady beat\n[Chorus]\nWe keep on going, we find our way\nOne small step at a time, every day",
"sample_rate": 44100,
"bitrate": 256000
}
}'
A successful create returns a task id:
{"code": 200, "data": {"taskId": "task_abc123..."}}
Then poll until the task reaches a terminal state:
curl https://api.hiapi.ai/v1/tasks/task_abc123 \
-H "Authorization: Bearer $HIAPI_KEY"
While the job is running you'll see a non-terminal status; when it finishes, status becomes success and the audio URL appears in output:
{
"code": 200,
"data": {
"taskId": "task_abc123",
"status": "success",
"output": [{"url": "https://.../track.mp3"}]
}
}
Download the file promptly — output URLs are time-limited, so treat them as a pickup window, not permanent storage:
curl -o track.mp3 "https://.../track.mp3"
import os
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {
"Authorization": f"Bearer {os.environ['HIAPI_KEY']}",
"Content-Type": "application/json",
}
def create_task() -> str:
payload = {
"model": "minimax-music-1.5",
"input": {
"prompt": ("Uplifting acoustic pop, warm female vocals, "
"gentle guitar and light percussion"),
"lyrics": ("[Verse]\nMorning light on a quiet street\n"
"Coffee steam and a steady beat\n"
"[Chorus]\nWe keep on going, we find our way\n"
"One small step at a time, every day"),
"sample_rate": 44100,
"bitrate": 256000,
},
}
r = requests.post(API_BASE, headers=HEADERS, json=payload, timeout=60)
data = r.json()
task_id = (data.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed: {data}")
return task_id
def wait_task(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
task = (r.json().get("data") or {})
status = task.get("status")
if status == "success":
return task
if status == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(poll_s)
raise TimeoutError(f"task {task_id} still running after {timeout_s}s")
def download(url: str, path: str = "track.mp3") -> str:
r = requests.get(url, timeout=120)
r.raise_for_status()
with open(path, "wb") as f:
f.write(r.content)
return path
if __name__ == "__main__":
task_id = create_task()
print(f"submitted: {task_id}")
task = wait_task(task_id)
url = task["output"][0]["url"]
print(f"done, downloading: {url}")
print(f"saved to {download(url)}")
Run it with python3 make_song.py. The script submits the job, polls every 5 seconds, and saves the finished track locally.
For anything beyond a quick script, register a callback so hiapi pushes the result to you when the task hits a terminal state. Add a top-level callback object (it sits next to model, not inside input):
{
"model": "minimax-music-1.5",
"input": {"prompt": "...", "lyrics": "..."},
"callback": {"url": "https://your.domain.example/hiapi/callback", "when": "final"}
}
Only "when": "final" is supported — you get one POST when the task succeeds or fails, not incremental progress events. Polling is fine for development and one-off jobs; callbacks win when you're generating many tracks or running behind a queue, because you hold no open loops and hit no rate limits from tight polling. If your callback never arrives, work through the checklist in why your hiapi task callback isn't firing.
Task creation is not idempotent by itself: retrying a timed-out POST /v1/tasks can create two jobs (and two charges). Persist the taskId as soon as the create call returns, and on restart resume by polling that id rather than blindly re-submitting. If a task seems stuck, don't kill and re-create it immediately — see when a hiapi /v1/tasks job hangs or times out for how to tell a slow job from a dead one.
The two failure surfaces look different:
permission_denied error ("This API key cannot use the selected model...") with a request_id you can quote to support. Schema violations return 400 INVALID_REQUEST with a precise message, e.g. prompt: minLength: got 1, want 10.status: "fail" with an error.code and error.message — the create call succeeded, but generation didn't. These are the ones to route to retry logic.Validate lengths client-side before submitting: prompt maxes out at 300 characters and lyrics at 600, and the API rejects oversized inputs rather than truncating them.
MiniMax Music 1.5 is billed per generated track on usage-based pricing — check the current rate on the hiapi pricing page and the minimax-music-1.5 model page, where you can also try the model in the playground before writing any code.
Exactly four: prompt (required, 10–300 chars), lyrics (required, 10–600 chars), sample_rate (optional, 16000/24000/32000/44100), and bitrate (optional, 32000/64000/128000/256000). Anything else is rejected with additional properties not allowed.
Yes — structure your lyrics with section tags like [Verse] and [Chorus]. The model uses them to shape the arrangement. Keep the whole lyrics string within the 600-character limit, tags included.
The input schema has no instrumental switch. The endpoint is lyrics-driven, so describe the instrumentation and mood you want in prompt; for purely instrumental use cases, check the model page for the current capability list.
When the task status is success, data.output[0].url holds the download link. Fetch it right away and store the bytes yourself — output URLs expire.
Poll for development and single jobs (a 5-second interval is plenty). Use callback with when: "final" in production so you don't hold connections open — especially if you batch-generate tracks.
Your key is invalid, or it doesn't have access to this model. Verify the key in your dashboard and make sure you're sending it as Authorization: Bearer sk-....
Key Takeaways