Tutorial
How to Call gpt-image-2/text-to-image@ext via the HiAPI API: curl and Python
A working request walkthrough for developers integrating text-to-image generation
What gpt-image-2/text-to-image@ext gives you
gpt-image-2/text-to-image@ext is a text-to-image model exposed through the HiAPI unified interface. You send a prompt plus optional generation parameters to one endpoint, and you get generated image data back. There is nothing model-specific to install, no per-vendor SDK, and no separate account to manage — the same API key and request shape cover the rest of the platform.
Reach for this model when your product needs to turn a written description into a fresh image: marketing copy to hero art, user prompts to thumbnails, design tooling that renders concepts on demand. Because the call is a plain HTTP request, it drops into a backend service, a queue worker, or a script without dragging in a client library.
It is the right pick when the input is text and the output is a raster image you intend to store and serve yourself. If you later need a different image route on the platform, the integration logic — auth, request envelope, output handling — stays put; only the model identifier and parameter block change.
How to read this guide
The walkthrough builds up in layers. First a minimal request that returns an image, then the Python translation, then the structural decision between a single call and async polling, and finally output handling. Each layer is independently testable, so when something breaks you know which change introduced it.
Prerequisites and authentication
Before the first request you need two things: a HiAPI account and an API key tied to it. Create the key in your account dashboard and store it the way you would store any secret — an environment variable locally, a secrets manager in production. Do not commit it, and do not paste it into client-side code that ships to browsers.
Authentication is handled with a bearer token in the request header. The shape is always the same:
Authorization: Bearer $HIAPI_API_KEY
Set the variable once in your shell so the examples below can reference it without echoing the key:
export HIAPI_API_KEY="your-key-here"
What to verify before you start
- The key is active and has image generation access.
- Your environment can make outbound HTTPS requests to the HiAPI endpoint.
- You know where generated files will be written — a local directory for testing, object storage for production.
If the key is missing or malformed, the failure surfaces immediately as an auth error on the first call, which is the cleanest possible time to find out.
A minimal working curl request
Start with the smallest request that produces an image. Everything beyond a prompt is optional, and keeping the first call bare means any error is almost certainly about auth or request shape rather than a parameter conflict.
curl -sS -X POST "$HIAPI_BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2/text-to-image@ext",
"prompt": "A weathered brass compass resting on a folded paper map, soft window light, shallow depth of field",
"size": "1024x1024",
"n": 1
}'
Field by field
model— the model identifier. This is the one value that ties the request togpt-image-2/text-to-image@ext; keep it exact.prompt— the description. Be concrete about subject, style, and lighting; vague prompts produce vague images and are harder to debug than parameter errors.size— the output dimensions. Omit it to accept the default while you are still validating the pipeline.n— how many images to generate per call. Keep it at one during integration so a failure maps to exactly one output.
Write the response to a file so you can inspect it without the terminal mangling binary data:
curl -sS -X POST "$HIAPI_BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d @payload.json -o response.json
Once this returns an image, you have a known-good baseline. Every later change should be a single, revertible edit away from it.
Porting the request to Python
The Python version is the same request with a different transport. Use requests — it is the least surprising choice and maps one-to-one onto the curl fields you already validated.
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"]
payload = {
"model": "gpt-image-2/text-to-image@ext",
"prompt": "A weathered brass compass resting on a folded paper map, soft window light",
"size": "1024x1024",
"n": 1,
}
response = requests.post(
f"{BASE_URL}/v1/images/generations",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json=payload,
timeout=120,
)
response.raise_for_status()
data = response.json()
The parts that matter
json=payloadsets the content type and serializes the body in one step — no hand-built strings, no escaping bugs.timeoutis not optional in practice. Image generation is slower than a typical JSON API call; a missing timeout means a hung worker when the upstream is slow.raise_for_status()turns HTTP failures into exceptions you can catch and classify, rather than letting a malformed body flow downstream.
Keep the payload as a dict, not an inline string. When you add parameters later, you add keys — the request shape does not change, and you never re-debug the transport.
If you prefer an SDK-style client, the same endpoint and headers apply; only the call syntax differs. Validate with curl first, then adopt whatever client your application already standardizes on.
Choosing between a single call and async polling
This is a design decision, not a syntax one, and it shapes your retry and storage logic more than any other choice in the integration.
Single call
Use a single request-and-response cycle when:
- You are generating on demand and a user is waiting.
- Your HTTP layer can tolerate a longer connection.
- You generate one image at a time rather than batches.
Keep the timeout generous relative to your slowest observed generation, and treat a timeout as a retryable failure rather than a hard error.
Async polling
Choose async when you are batching, when requests originate from a queue worker, or when the caller must not block. The pattern is: submit, receive a handle, then poll for completion.
import time
def poll_until_done(fetch_status, handle, interval=2, max_wait=300):
deadline = time.monotonic() + max_wait
while time.monotonic() < deadline:
status = fetch_status(handle)
if status["state"] == "succeeded":
return status
if status["state"] == "failed":
raise RuntimeError(status.get("error", "generation failed"))
time.sleep(interval)
raise TimeoutError("generation did not finish in time")
Rules for a polling loop that behaves
- Back off. Start with a short interval and increase it; constant tight polling wastes quota and rate budget.
- Cap the wait. A
max_waitdeadline prevents orphaned jobs from pinning a worker forever. - Persist the handle. If the worker restarts, you must be able to resume polling rather than resubmitting and paying for a duplicate generation.
- Treat failure as terminal per job. A
failedstate should be recorded, not silently retried in a tight loop.
Decide this early. Retrofitting async semantics onto a synchronous code path later means rewriting callers, storage, and error handling at once.
Handling the image output
The response contains your generated image, but it is data inside a JSON envelope — not a URL to hotlink. Treat it as binary output you own.
Decode and persist
import base64
item = data["data"][0]
image_bytes = base64.b64decode(item["b64_json"])
with open("out.png", "wb") as f:
f.write(image_bytes)
If the response carries a URL instead of inline data, fetch it immediately and write the bytes to your own storage. Generated URLs are transient; anything you serve to users should live behind your infrastructure.
Verify before you serve
- Check the magic bytes. A PNG starts with
\x89PNG; an HTML error page does not. A few bytes of validation catches proxy errors that return HTTP 200 with the wrong body. - Record the size. Log byte length so truncated downloads are visible in monitoring.
- Store deterministically. Name files by a generation ID, not by prompt text, and keep the prompt, parameters, and response metadata alongside the image. That bundle is what lets you reproduce or audit any output later.
- Set content type correctly when serving — derive it from the actual file, not from an assumption.
Keep the metadata with the artifact
Write a small sidecar record: model identifier, full prompt, size and count, timestamp, and any request ID from the response. Six months from now, when someone asks why an image looks the way it does, that record is the only answer you will have.
Debugging common failures
Most first-integration failures fall into three buckets. Check them in this order — it is roughly cheapest to most expensive to diagnose.
Authentication errors
A 401 or 403 means the key is wrong, revoked, or not being sent. Echo the header construction without the secret to confirm the Bearer prefix and spacing are intact, and confirm the environment variable is actually populated in the process making the request — a common trap is setting it in your shell but not in the worker or container.
Malformed payloads
A 400 usually points at the body. Revert to the minimal request from the curl section and add fields back one at a time. The usual culprits:
- The
modelstring does not matchgpt-image-2/text-to-image@extexactly. - The body is not valid JSON — a trailing comma or unescaped quote.
- A parameter is the wrong type;
nis a number, not a string. Content-Type: application/jsonis missing, so the server cannot parse the body.
Timeouts and slow responses
If the connection drops before an image returns, you are likely exceeding your client timeout rather than hitting a model failure. Raise the timeout for a single-call design, or move to async polling for anything batch-shaped. On retries, make sure you are not double-generating: if a request may have succeeded, check for an existing result before resubmitting.
When it is none of the above
Capture the full response — status code, headers, and body — for the failing request, and reproduce it with the minimal curl command. A failure that survives the minimal request is an integration problem in your environment; one that disappears points at the parameters you added.
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