HiAPI
Blog

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

Oct 31, 2026 9 min read
黑绿底上,黄铜色与深绿色扁平缎带以经纬交织方式构成开放式网格,这是教程文章的封面图。

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 to gpt-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=payload sets the content type and serializes the body in one step — no hand-built strings, no escaping bugs.
  • timeout is 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_wait deadline 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 failed state 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 model string does not match gpt-image-2/text-to-image@ext exactly.
  • The body is not valid JSON — a trailing comma or unescaped quote.
  • A parameter is the wrong type; n is a number, not a string.
  • Content-Type: application/json is 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 →

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