Tutorial
How to Call muse-image-1.0/text-to-image via the HiAPI API: curl and Python
What this integration actually involves
A text-to-image call on HiAPI is a single synchronous HTTP request: you send a JSON body describing the image you want, and the response comes back containing the generated image data. There is no polling loop or job queue to manage for this endpoint — one request, one response.
The moving parts are few:
- Your API key — authenticates the request via an
Authorizationheader. - The endpoint — a fixed URL path for
muse-image-1.0/text-to-imageon the HiAPI base URL. - The JSON body — carries your prompt and any optional parameters the model accepts.
- The response payload — returns the generated image data, which you decode or forward.
That's the whole surface area. The sections below build the request piece by piece with curl, then repeat it in Python, so you can verify each part before adding complexity.
Prerequisites and authentication
Before your first request, make sure you have:
- An API key from your HiAPI account dashboard. Treat it like a password; never commit it to source control.
- An HTTP client —
curlis fine for testing; Python 3.8+ is the target for the script version. - The base URL for the HiAPI API, which you'll find alongside your key in the dashboard docs.
Authentication uses a bearer token in the Authorization header. Every request to muse-image-1.0/text-to-image needs it:
Authorization: Bearer $HIAPI_API_KEY
Store the key in an environment variable rather than hard-coding it in scripts:
export HIAPI_API_KEY="your-key-here"
The endpoint path for this model is /muse-image-1.0/text-to-image, appended to the base URL. The exact base URL is documented in your HiAPI dashboard; keep it in an environment variable too, so you can switch environments without editing code:
export HIAPI_BASE_URL="https://api.hiapi.ai" # confirm the current value in your dashboard
With those two variables set, you're ready to send a request.
A working request with curl
Here is a minimal muse-image-1.0/text-to-image call. It sends a prompt and writes the raw JSON response to a file so you can inspect it before writing any parsing code.
curl -sS -X POST "$HIAPI_BASE_URL/muse-image-1.0/text-to-image" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a quiet harbor at dawn, soft fog, wide angle",
"n": 1
}' \
-o response.json
A few things to note about the body:
promptis required — it's the text description the model renders.nis optional and controls how many images you request. Omit it to accept the default.- Only include parameters you've confirmed in the HiAPI API reference for this model. Don't guess at undocumented fields; an unknown parameter may be rejected or silently ignored depending on the server's validation.
If the request succeeds, response.json contains the image data. If it fails, you get an error body and a non-2xx status code — see the error-handling section below.
You can check the HTTP status without dumping the whole body:
curl -sS -o /dev/null -w "%{http_code}\n" -X POST "$HIAPI_BASE_URL/muse-image-1.0/text-to-image" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"test"}'
The same request in Python
The Python version uses requests and mirrors the curl call exactly — same endpoint, same headers, same body. Lift it into a function you can reuse.
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"]
def generate_image(prompt: str, n: int = 1) -> dict:
url = f"{BASE_URL}/muse-image-1.0/text-to-image"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {"prompt": prompt, "n": n}
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status() # raise on 4xx/5xx
return response.json()
if __name__ == "__main__":
result = generate_image("a quiet harbor at dawn, soft fog, wide angle")
print(result.keys())
Two details worth keeping:
timeout=60— set an explicit timeout. Image generation is slower than a typical JSON API call; without a timeout your process can hang indefinitely on a stalled connection.raise_for_status()— surfaces HTTP errors as exceptions rather than letting you silently parse an error body as if it were an image.
If you're already using httpx or aiohttp, the translation is direct: same headers, same JSON body. Nothing in this request depends on a specific HTTP library.
Reading the synchronous response
Because muse-image-1.0/text-to-image is synchronous, the image data arrives in the same response — there's no second call to fetch a result. The response is JSON; inspect its structure once before writing a parser:
import json
result = generate_image("a quiet harbor at dawn")
print(json.dumps(result, indent=2)[:500])
How you handle the image depends on what the response carries:
If the response contains base64 image data
Decode the string and write the bytes to a file:
import base64
b64 = result["data"][0]["b64_json"] # field name: confirm against the API reference
with open("output.png", "wb") as f:
f.write(base64.b64decode(b64))
If the response contains a URL
Download it with a plain GET — the URL is already authorized for retrieval, so don't attach your API key to third-party hosts:
import requests
image_url = result["data"][0]["url"] # field name: confirm against the API reference
img = requests.get(image_url, timeout=60)
img.raise_for_status()
with open("output.png", "wb") as f:
f.write(img.content)
The exact field names — b64_json versus url, and the wrapper around them — are defined in the HiAPI API reference for this model. Print the response once, confirm the shape, then code against it. Don't assume the structure from a different model.
In a service, don't write to local disk blindly: stream the bytes to object storage, or pass them directly to whatever consumes the image (an upload step, a queue, a response to your own client). Validate that the decoded bytes look like an image (check the magic bytes) before forwarding.
Handling error codes and retries
HiAPI returns standard HTTP status codes. The useful triage rule is: can the same request succeed if I send it again unchanged?
Fix the request — don't retry
400 Bad Request— malformed JSON or a parameter the model doesn't accept. Check thatpromptis present and that you haven't invented fields. Fix the body; retrying identically will fail identically.401 Unauthorized— missing or invalid API key. VerifyHIAPI_API_KEYis set and hasn't been rotated.403 Forbidden— the key is valid but not permitted for this endpoint. Check your account's access in the dashboard.404 Not Found— the path is wrong. Confirm the endpoint string is exactly/muse-image-1.0/text-to-imageand the base URL is correct.422 Unprocessable Entity— well-formed JSON but semantically invalid, e.g. an out-of-range value. Read the error message; it usually names the field.
Retry with backoff
429 Too Many Requests— you're being rate-limited. Back off exponentially and retry; respect anyRetry-Afterheader if present.5xxerrors — server-side failures. Retry with exponential backoff and a cap (e.g. three attempts), then give up and surface the error.
A compact retry loop:
import time
def post_with_retry(url, headers, payload, attempts=3):
for i in range(attempts):
resp = requests.post(url, headers=headers, json=payload, timeout=60)
if resp.status_code == 429 or resp.status_code >= 500:
time.sleep(2 ** i)
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError(f"failed after {attempts} attempts: {resp.status_code} {resp.text}")
Always log the status code and the error body on failure. The message in the body is what tells you which of the two buckets a given error belongs to.
Production checklist before you ship
A few habits keep muse-image-1.0/text-to-image calls predictable once they leave your notebook:
- Pin the request shape. Validate the JSON body against the fields documented in the API reference. If you add a parameter, confirm it's supported first.
- Set timeouts everywhere. Both the generate call and any download of returned image data need explicit timeouts.
- Separate error classes. Distinguish retryable (429, 5xx) from terminal (400, 401, 403, 404, 422) failures so your alerts aren't noisy and your retries aren't wasted.
- Log the essentials. Request ID if the response provides one, status code, latency, and the prompt's length — not the prompt itself if it may contain sensitive text.
- Watch cost drivers. Generation is billed per output — the number of images, their size, and how often you retry all contribute. Avoid retrying terminal errors, and avoid re-generating when a cached result would do.
- Keep keys server-side. Never call the endpoint directly from a browser or mobile client with your API key embedded.
With those in place, the integration is a small, boring function you can call from anywhere — which is exactly what you want. For the full parameter surface and current response fields, check the HiAPI API reference for this model.
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