HiAPI
Blog

Tutorial

How to Call the mirelo-sfx-2.0 API via HiAPI: curl, Python, and Async Polling

Nov 3, 2026 10 min read
HiAPI 教程文章封面图:近黑底上薄荷绿半调点阵由疏至密铺展,深青点缀,构图克制均衡。

What mirelo-sfx-2.0 Does and When to Reach for It

mirelo-sfx-2.0 is a text-to-sound-effects model available through HiAPI. You give it a natural-language description—such as "heavy wooden door creaking open slowly" or "coin dropped on a marble table"—and it generates a short audio clip that matches the prompt.

This is the endpoint you reach for when you need sound design that is generated on demand rather than licensed from a sample library:

  • Game and app prototyping: generate placeholder or final sound effects for interactions, UI events, and ambient loops.
  • Video and podcast post-production: fill gaps with Foley-style audio without booking a recording session.
  • Generative pipelines: feed prompts from a script, a storyboard, or user input and attach the resulting WAV to the output.

The model fits best when the sound is described clearly and is short in duration. If you need music, speech, or long-form audio, that is a different model family—mirelo-sfx-2.0 is focused on discrete sound effects.

Because generation is asynchronous, plan for a job-based integration rather than a blocking HTTP call. The rest of this tutorial builds that integration from a raw curl request up to a Python client that polls for the finished WAV.

Prerequisites and Authentication Setup

Before your first call, make sure you have:

  • A HiAPI account and API key. You can create and manage keys in the HiAPI dashboard. Treat the key like a password: load it from an environment variable or a secrets manager, and never commit it to a repository.
  • A tool to send HTTP requests. curl is enough for the walkthrough; Python 3.8+ is used for the code examples.
  • An HTTP client in Python. The examples use requests for brevity, but any client that can send JSON and headers will work.

HiAPI uses a single base URL and a single authentication style for all models, so the conventions you learn here apply to other endpoints as well:

export HIAPI_API_KEY="your-api-key-here"

Every request sends the key in the Authorization header:

Authorization: Bearer $HIAPI_API_KEY
Content-Type: application/json

The model identifier is passed in the request body as "model": "mirelo-sfx-2.0". There is no separate SDK to install—if your HTTP client can reach the HiAPI base URL, you can call the model.

One practical note: keep the base URL in a config variable rather than hard-coding it across files. When you move from a local script to a staging or production service, you will want to change it in one place.

The curl Request, Step by Step

The generation request is a single authenticated HTTP POST to the HiAPI task-creation endpoint. Here is a complete invocation:

curl -sS -X POST "$HIAPI_BASE_URL/v1/tasks" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mirelo-sfx-2.0",
    "input": {
      "prompt": "heavy wooden door creaking open slowly, old house ambience"
    }
  }'

Breaking down each part:

  • -X POST — task creation is a write operation. A GET to this path will not start a job.
  • The URL — the endpoint accepts new generation tasks. The base URL lives in HIAPI_BASE_URL so the same command works against any environment.
  • Authorization — the bearer token identifies your account and is what gets billed. A missing or malformed header is the most common cause of a 401.
  • Content-Type: application/json — the body is JSON; servers will reject or misparse the payload without this header.
  • model — selects mirelo-sfx-2.0. Because HiAPI routes on this field, you can switch models without changing the URL.
  • input.prompt — the text description of the sound. Concrete, physical descriptions (materials, actions, environment) produce better results than abstract ones.

A successful submission does not return the audio. It returns a task object with an identifier—typically an id field—that you use to check status. Capture that ID from the response; that is the thread you pull to retrieve the WAV later.

If you want to confirm the request shape before wiring it into code, run this exact command in a terminal first. Once it succeeds, translating it to Python is mechanical.

The Same Call in Python

The Python version keeps the same URL, headers, and body—it just expresses them as a dictionary and a client call:

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]

def create_sfx_task(prompt: str) -> str:
    response = requests.post(
        f"{BASE_URL}/v1/tasks",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": "mirelo-sfx-2.0",
            "input": {"prompt": prompt},
        },
        timeout=30,
    )
    response.raise_for_status()
    return response.json()["id"]

What differs from the shell version:

  • Environment access — os.environ replaces shell expansion, so the key never appears in source code.
  • json= instead of -d — requests serializes the dictionary and sets the content type for you. You can drop the manual header, though keeping it is harmless.
  • raise_for_status() — turns non-2xx responses into exceptions, which is where your error handling belongs.
  • timeout — a client-side timeout prevents a stalled connection from hanging your worker. Task creation is fast; the long wait happens during polling.

The return value is the task ID. Store it—it is the only handle you have on the generation. From here the Python path and the curl path converge: both need a polling step before the audio exists.

Because the request body is just data, you can parameterize the prompt and loop over a list of descriptions to generate a batch of effects, creating one task per prompt and collecting the IDs.

Handling the Async Task and Polling for Results

Sound generation takes time—seconds, not milliseconds—so HiAPI models it as a task. The creation call returns immediately with an ID; a second endpoint reports whether that task is still running, finished, or failed.

The typical loop is:

  1. Create the task and capture the ID.
  2. Poll the task-status endpoint with that ID.
  3. If the state is still processing, wait briefly and poll again.
  4. When the state reports success, read the output from the response.
  5. If the state reports failure, stop and surface the error.

In Python:

import time

def wait_for_task(task_id: str, timeout_s: int = 300) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        response = requests.get(
            f"{BASE_URL}/v1/tasks/{task_id}",
            headers={"Authorization": f"Bearer {API_KEY}"},
            timeout=30,
        )
        response.raise_for_status()
        task = response.json()

        status = task.get("status")
        if status == "succeeded":
            return task
        if status == "failed":
            raise RuntimeError(task.get("error", "task failed"))

        time.sleep(2)

    raise TimeoutError(f"task {task_id} did not finish in time")

A few things worth calling out:

  • Poll interval. Two seconds is a reasonable default for sound effects. Polling in a tight loop wastes quota and can trip rate limits without making the result arrive any faster.
  • Overall deadline. Always bound the loop. A stuck task should fail your job, not hang a worker forever.
  • Status values. Treat unknown status values as "still processing" rather than assuming success, and branch explicitly on the success and failure states.
  • Webhooks, if offered. If your HiAPI account supports completion callbacks, prefer them over polling in server-side production code; polling remains the simplest option for scripts and prototypes.

Keep the task ID in your own database alongside the prompt so you can retry, audit, or re-fetch an output later without regenerating it.

Reading the Returned WAV Response

When the task reports success, the response contains the generated audio. Depending on the endpoint version, the output appears either as a URL to the WAV file or as inline audio data; treat both cases as "bytes you can store."

The successful task object carries the output under a field such as output, and typically includes metadata like the model name, the original prompt, and timing information. The audio itself is a standard WAV container, which means you can hand it to any audio library without a custom decoder.

To persist a URL-based result:

result = wait_for_task(task_id)
audio_url = result["output"]["audio_url"]

wav_bytes = requests.get(audio_url, timeout=60).content
with open(f"{task_id}.wav", "wb") as f:
    f.write(wav_bytes)

If the audio arrives as decoded data instead, base64-decode it before writing:

import base64

wav_bytes = base64.b64decode(result["output"]["audio_base64"])
with open(f"{task_id}.wav", "wb") as f:
    f.write(wav_bytes)

Once you have the bytes:

  • Validate the header. A WAV file starts with the RIFF magic bytes. Checking the first four bytes catches truncated downloads before they reach storage.
  • Store it durably. Write to object storage and keep the key alongside the task ID in your database. Treat generated audio as a cacheable artifact, not a transient response.
  • Play or process it. Standard tools—wave, soundfile, or any audio player—can open the file directly. From there you can trim silence, normalize loudness, or convert to another format for delivery.

Because the file is ordinary WAV, wiring it into a web endpoint or a game engine asset pipeline requires no special handling.

Common Failure Points and How to Fix Them

Most integration problems fall into a few predictable categories.

Authentication errors (401 / 403)

A 401 almost always means the Authorization header is missing, malformed, or built from an empty environment variable. Confirm that HIAPI_API_KEY is exported in the shell where the process runs—not just in your editor—and that the header reads Bearer <key> with a single space. A 403 usually means the key is valid but lacks access to the model; check your account's model permissions.

Malformed payloads (400 / 422)

The most common cause is a body that is not valid JSON—an unbalanced quote in a curl command is a classic. Validate with a JSON linter before sending. The second most common cause is a missing model field or a prompt nested at the wrong level; the model expects input.prompt, not a top-level prompt. Finally, check that the prompt is non-empty and that its length falls within the model's documented limits.

Polling too aggressively

A tight while loop with no sleep generates a burst of status requests that can hit rate limits and slow down the very task you are waiting on. Use a fixed interval of a second or two, add jitter if you run many jobs in parallel, and always enforce a maximum wait time.

Treating creation as completion

The creation response looks like a success and returns 200, but it contains no audio. If your code immediately reads an output field, it will fail or write an empty file. Always route through the status endpoint before touching the result.

Silent timeouts and retries

Network timeouts on task creation are safe to retry, but retrying a creation call after a timeout that actually succeeded creates a duplicate job. Use idempotency keys if the endpoint supports them, or reconcile by listing recent tasks before retrying.

Disk and storage edge cases

When writing WAV bytes, open files in binary mode ("wb"). Text mode corrupts the header. And if you store outputs by prompt name, two tasks with the same prompt will overwrite each other—key files by task ID instead.

With those guards in place, an mirelo-sfx-2.0 integration is a short pipeline: create, poll, persist. Start from the curl call, port it to Python, and keep the task ID as your source of truth.

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