HiAPI
Blog

Tutorial

How to Use mureka-9.5/instrumental via the HiAPI API: curl and Python Guide

Nov 3, 2026 11 min read
棕褐与燕麦色调的宽织物条带交织封面图,呈扁平织机图式的篮纹节奏,点缀克制的黄铜色细条。

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. The model field names the endpoint; the input object 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=30 on 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

  1. Submit the job, capture the ID.
  2. Sleep briefly.
  3. Query status for that ID.
  4. If status is queued or processing, back off and repeat.
  5. If status is a success state, read the output.
  6. 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 →

Start building with persistent AI artifacts

Get your API key and call image, video and audio models through one task-based API.

Read the API docs