Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
lyria-3-pro is a music generation model available through the hiapi API: send a text prompt, get back a finished audio track. This guide walks through a request that actually returns a taskId, the full (very small) input schema, and the production details you need once you move past a one-off test.
sk-.curl or Python's requests. No SDK is required; hiapi speaks plain REST.lyria-3-pro runs on hiapi's unified async task API: you POST a task, get a taskId back immediately, then either poll for the result or receive a callback when the track is ready.
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "lyria-3-pro",
"input": {
"prompt": "Warm lo-fi piano loop with soft vinyl crackle, relaxed tempo, calm evening mood"
}
}'
A healthy response returns immediately, before the track is actually rendered:
{
"code": 200,
"message": "success",
"data": { "taskId": "tk-hiapi-01M0T17T7RERV481QJC3DRZCRS" }
}
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01M0T17T7RERV481QJC3DRZCRS" \
-H "Authorization: Bearer sk-YOUR_API_KEY"
Wait a couple of seconds before the first poll, then check every 3–5 seconds. status moves through intermediate states like archiving before landing on a terminal state:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01M0T17T7RERV481QJC3DRZCRS",
"model": "lyria-3-pro",
"status": "success",
"created": 1787580115,
"completed": 1787580157,
"output": [
{
"type": "audio",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M0T17T7RERV481QJC3DRZCRS-0.mp3",
"expireAt": 1788184956
}
]
}
}
Download output[0].url promptly — the default temp storage tier expires after about 7 days (expireAt is a Unix timestamp). On failure, status is fail and data.error holds { code, message } instead of output.
import time
import requests
API_KEY = "sk-YOUR_API_KEY"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
payload = {
"model": "lyria-3-pro",
"input": {
"prompt": "Warm lo-fi piano loop with soft vinyl crackle, relaxed tempo, calm evening mood",
},
}
create = requests.post("https://api.hiapi.ai/v1/tasks", headers=HEADERS, json=payload)
task_id = create.json()["data"]["taskId"]
while True:
time.sleep(3)
detail = requests.get(f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=HEADERS).json()
status = detail["data"]["status"]
if status == "success":
print(detail["data"]["output"][0]["url"])
break
if status == "fail":
raise RuntimeError(detail["data"]["error"])
input accepts exactly two fields — anything outside this list gets rejected with 400 INVALID_REQUEST:
| Field | Type | Required | Notes |
|---|---|---|---|
prompt | string | yes | Describe genre, instrumentation, mood, and tempo — the more specific, the more consistent the result. |
seed | integer | no | Minimum 0. Reuse the same seed with the same prompt for a more repeatable take. |
prompt is required — omit it and the task never gets created:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: prompt: missing required field \"prompt\""}
Send anything else — duration, negative_prompt, sample_count, and similar fields some other music models accept — and the request is rejected outright:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: <root>: additional properties 'negative_prompt' not allowed"}
The output track's length is fixed by the model itself; there's no duration parameter to configure it.
Callbacks over polling. For anything beyond a quick test, pass a top-level callback instead of polling in a loop:
{
"model": "lyria-3-pro",
"callback": { "url": "https://your-domain.com/hiapi/callback", "when": "final" },
"input": { "prompt": "..." }
}
hiapi POSTs to callback.url once when the task reaches a terminal state (success or fail) — no need to hold a polling loop open. Keep polling as a fallback in case a callback delivery fails.
Idempotency. Send an Idempotency-Key header (up to 255 bytes) if your caller might retry the same request — a retry with the same key on the same account returns the original taskId instead of creating (and billing) a duplicate track.
Keep output past 7 days. Output storage defaults to temp (~7 days). Set a top-level "storage": "persistent" on creation to keep the track long-term (billed by size), or promote a temp output afterward.
Error handling. A bad or unauthorized key returns HTTP 401:
{"error":{"code":"permission_denied","message":"This API key cannot use the selected model. Please check permissions or use another key.","type":"hiapi_error"}}
Malformed input returns 400 with error_code: INVALID_REQUEST (as shown above); insufficient balance returns 402; a non-JSON Content-Type returns 415. If you get an intermittent 503, retry with backoff — it means the platform is momentarily unavailable, not that your request is wrong.
Pricing for lyria-3-pro (and every other model on the platform) is listed on the hiapi pricing page rather than hardcoded here, since tiers can change.
Can I set how long the generated track is?
No. lyria-3-pro's input schema only accepts prompt and seed — there's no duration field, and the model determines the track length itself.
Does lyria-3-pro support a negative prompt or style reference audio?
No. Unlike some other models on the platform, lyria-3-pro's schema is strict to prompt and seed only; sending negative_prompt or any reference-audio field returns 400 INVALID_REQUEST.
What format is the output file?
An MP3, returned as output[0].url with type: "audio". The URL is a temporary link that expires per output[0].expireAt unless you set "storage": "persistent".
How do I get a repeatable result?
Pass the same seed (a non-negative integer) with the same prompt. Omit seed and each call can produce a different take.
What's the fastest way to debug a request that keeps returning 400?
Read message — it names the specific missing or invalid field. The schema is strict: sending a field this model doesn't accept (like duration, which other music models on the platform do accept) returns 400 with an "additional properties not allowed" error.