Tutorial
How to Use mureka-9.5/instrumental via the HiAPI API: curl and Python Guide
What mureka-9.5/instrumental Does on HiAPI
mureka-9.5/instrumental is a text-to-music endpoint on HiAPI tuned for instrumental output — background music, loops, beds, and underscore. You describe the track in a prompt, send a single job, and HiAPI returns a finished audio asset you can download and drop into a game, video edit, podcast, or product demo.
The word instrumental is the important part. This endpoint is meant for music without a lead vocal performance, so it fits scenes where narration or dialogue has to stay intelligible. If you need a sung track with lyrics, that is a different problem; if you need a clean bed under a voiceover, this is the right tool.
Pick this endpoint when:
- You want generated BGM rather than sourced library music.
- You need many variations quickly and programmatically, not one bespoke commission.
- Your pipeline already speaks HTTP and you want to treat music like any other asset generation step.
- You care about a predictable request shape: submit, poll, download.
Like other music generation endpoints, work is asynchronous. A submission returns a job you then track until it produces audio. Design your code around that from the start and the rest of the integration is routine.
Where it fits in a product
Typical consumers are content pipelines that need a track on demand: a video renderer that wants a bed under each clip, a game that wants ambient loops, or a prototyping tool that lets a user type a mood and hear a result. Because the endpoint is text-driven, you can template prompts from metadata you already have — genre, tempo feel, mood, and intended length — and generate at render time.
Treat each request as one generation job with its own lifecycle. That framing keeps your retry logic, logging, and cost accounting sane.
Before You Send a Request
A few things need to be true before the first call works. None of them are exotic, but skipping one is the most common reason a first request fails.
Credentials
You need a HiAPI API key. Treat it as a secret: load it from an environment variable such as HIAPI_API_KEY, never hard-code it into a script you commit, and rotate it if it leaks. The key is sent in the Authorization header as a bearer token.
export HIAPI_API_KEY="your_api_key_here"
Endpoint and transport
You are calling the mureka-9.5/instrumental model through the HiAPI unified API. The base URL is the HiAPI API host; the model identifier goes in the request payload or path per HiAPI's documented convention for this endpoint. Use HTTPS, send Content-Type: application/json, and send a JSON body.
Do not guess at parameter names. HiAPI documents the accepted fields for this model; anything outside that set is at best ignored and at worst rejected. If a field you want is not documented, it is not part of the contract — build around what is.
What to prepare
- A prompt that describes the music: genre, instrumentation, mood, tempo feel, and intended use.
- Any documented options for length or variation that the model exposes.
- A place to store the job identifier and, later, the download URL.
- Network access to the HiAPI host from wherever your code runs.
A note on time
Generation is not instant. Set client-side timeouts generously and expect to poll. Synchronous thinking — send a request and block until audio comes back in the same response — will lead to flaky code.
A Working curl Request
Start with curl. It removes every layer between you and the HTTP conversation, so if something is wrong you see it immediately.
The call
curl -sS -X POST "$HIAPI_BASE_URL" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mureka-9.5/instrumental",
"input": {
"prompt": "warm lo-fi hip hop instrumental, mellow electric piano, soft vinyl crackle, relaxed 80 bpm, no vocals"
}
}'
Set the two environment variables first so nothing sensitive lives in your shell history in plaintext beyond what you choose:
export HIAPI_BASE_URL="https://api.hiapi.ai/v1/..." # use the documented path for this endpoint
export HIAPI_API_KEY="your_api_key_here"
What each part does
-X POST— submission is a create operation.-H "Authorization: Bearer ..."— authenticates the call against your account.-H "Content-Type: application/json"— tells the server the body is JSON; omit it and many servers refuse the body outright.-d '{...}'— the JSON payload. Themodelfield names the endpoint; theinputobject carries your prompt and any other documented fields.-sS— quiet progress, but still show errors. Useful when scripting.
Read the response
The response is JSON. You are looking for a job or task identifier and a status field. Print it prettily so you can inspect it:
curl -sS -X POST "$HIAPI_BASE_URL" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d @payload.json | jq .
Keep the identifier. It is the handle for every subsequent status check. Don't parse the whole body into your app; extract the job ID and the status and store the raw response for debugging.
If you get a non-2xx status, read the body — HiAPI returns a structured error message that usually names the offending field.
The Same Request in Python
The Python version mirrors the curl call exactly: same headers, same JSON body. Use requests for brevity, or the standard library if you want zero dependencies.
With requests
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"]
payload = {
"model": "mureka-9.5/instrumental",
"input": {
"prompt": (
"warm lo-fi hip hop instrumental, mellow electric piano, "
"soft vinyl crackle, relaxed 80 bpm, no vocals"
)
},
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
resp = requests.post(BASE_URL, json=payload, headers=headers, timeout=30)
resp.raise_for_status()
data = resp.json()
job_id = data["id"] # use the documented field name
print("submitted:", job_id, data.get("status"))
With the standard library
If you cannot add a dependency:
import json, os, urllib.request
req = urllib.request.Request(
os.environ["HIAPI_BASE_URL"],
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
Practical habits
timeout=30on submission only. Polling needs its own loop and its own timeout.raise_for_status()before parsing. An error body is not a job.- Wrap the job ID extraction in a small helper so a field-name change is a one-line fix.
- Never log the API key; log the job ID and status instead.
- Keep the payload as a dict in code, serialized once, so tests can assert on it.
Handling the Async Job and Polling
Submission is step one. Generation happens server-side and completes later, so you poll a status endpoint until the job reports a terminal state.
The loop shape
- Submit the job, capture the ID.
- Sleep briefly.
- Query status for that ID.
- If status is queued or processing, back off and repeat.
- If status is a success state, read the output.
- If status is a failure state, log the error and stop — do not retry blindly.
A reliable implementation
import time, random, requests
def wait_for_job(job_id, base_status_url, headers,
timeout_s=600, initial_delay=2.0, max_delay=30.0):
deadline = time.monotonic() + timeout_s
delay = initial_delay
while time.monotonic() < deadline:
r = requests.get(f"{base_status_url}/{job_id}", headers=headers, timeout=30)
r.raise_for_status()
status = r.json()["status"]
if status in ("succeeded", "completed", "success"):
return r.json()
if status in ("failed", "error", "cancelled"):
raise RuntimeError(f"job {job_id} ended as {status}: {r.text}")
time.sleep(delay + random.uniform(0, 0.5)) # jitter
delay = min(delay * 1.5, max_delay) # exponential backoff
raise TimeoutError(f"job {job_id} did not finish in {timeout_s}s")
Why each detail matters
- Jitter prevents synchronized retries when many jobs run in parallel.
- Backoff keeps you from hammering the status endpoint while a track renders.
- A deadline turns an infinite hang into a clean exception your pipeline can handle.
- Terminal-state checks should match the status strings HiAPI documents. Confirm them against the API reference rather than trusting a guess.
- Retries belong around transient network errors (timeouts, 5xx), not around a job that already failed with a validation error.
Operational tips
Persist job_id before you start polling. If your process dies, you can resume polling instead of paying for a second generation. Log status transitions with timestamps so you can see where time goes. If you run many jobs, poll a handful in parallel via a queue rather than blocking a request thread on each one.
Getting Instrumental Output and Downloading It
Instrumental mode is expressed through your request, and the endpoint is dedicated to that kind of output. In practice, prompt wording reinforces it: name the instruments, describe the mood, and state that there is no vocal. A prompt that asks for a vocal line is fighting the endpoint's purpose.
Where the audio appears
When the job reaches a success state, the status response carries the result. Expect the audio to be delivered as a URL you fetch separately, not as base64 inside the status JSON. Read the response and pull the download URL from the documented output field.
result = wait_for_job(job_id, status_url, headers)
# Inspect result carefully the first few times, then pin the field.
audio_url = result["output"]["audio_url"] # confirm field name in the API reference
with requests.get(audio_url, stream=True, timeout=60) as r:
r.raise_for_status()
with open(f"{job_id}.mp3", "wb") as f:
for chunk in r.iter_content(chunk_size=65536):
f.write(chunk)
Download habits
- Stream to disk. Music files are large; loading them fully into memory is avoidable pressure.
- Check the content type. Store the file with the extension implied by the response, not a hard-coded one.
- Treat URLs as temporary. Download promptly, or persist the URL only if the docs say it is stable. Copy the bytes into your own storage for anything durable.
- Fetch once. Downloading in a retry loop without a completion marker is an easy way to create partial files. Write to a temporary name and rename on success.
- Record provenance. Store the job ID and prompt alongside the file. When you need a variation later, you have the exact inputs.
Common Failure Points and Fixes
Most first-integration problems fall into a short list. Here is the checklist.
401 / 403 — authentication. The header is wrong, the key is missing, or an environment variable resolved to an empty string. Verify the key is set in the same shell or process that runs the code, and that the header reads exactly Authorization: Bearer <key>.
400 — malformed payload. A missing model, a typo in mureka-9.5/instrumental, invalid JSON, or a field the endpoint does not accept. Send the body with curl first to isolate the payload from your code. Validate JSON before sending; trailing commas are a classic.
415 or empty body errors — content type. Content-Type: application/json must be present. Some HTTP clients set it automatically when you pass json=, but not all.
Poll timeouts. Your deadline is too short, your backoff is too aggressive, or the job genuinely failed and you classified a failure status as still-running. Check the exact status strings against the docs and print every status transition while debugging.
Missing download URL. You are reading the wrong field, or you read the response before the job reached its terminal success state. Log the full success payload once, confirm the path to the audio URL, then pin it.
Duplicate generations. Your poller crashed and something resubmitted. Persist the job ID before polling and check for an existing job before submitting a new one.
Rate limits. Back off on 429 responses with jitter, and queue work rather than bursting from many threads at once.
Work through them in this order — auth, payload, content type, polling, output — and you will usually find the cause in one pass. Once a curl call and its Python twin both succeed end to end, the endpoint is doing exactly what you asked: turning a prompt into an instrumental track and handing you a URL to download it.
Related articles
View all articles →-
Tutorial
How to Call hailuo-2.3-fast/image-to-video via the HiAPI API: curl and Python
A first working request for the fast, low-cost image-to-video tier — from model ID to downloaded MP4
9 min read -
Tutorial
How to Use hailuo-2.3/text-to-video via the HiAPI API: curl, Python, and a Working Request
11 min read -
Tutorial
Kandinsky 6.0 Pro Text-to-Video on HiAPI: curl and Python Tutorial
A request-first walkthrough: build the prompt-to-clip call, submit it, poll for completion, and download the result
11 min read