Tutorial
How to Use recraft-v4.1/text-to-image via the HiAPI API: curl and Python Guide
What this endpoint gives you
recraft-v4.1/text-to-image is a text-to-image model endpoint: you send a text prompt plus a few shaping options in the request body, and you get generated image data back. It is a good fit when you want a single call to produce a finished image rather than orchestrating a multi-step pipeline yourself.
On HiAPI, the model fits into an aggregation model: one base URL, one authentication header, and a consistent request/response shape across endpoints. Instead of wiring up a separate SDK and credential set for every image model you evaluate, you point your client at the HiAPI endpoint for recraft-v4.1/text-to-image and keep the rest of your code stable. That matters most during prototyping, when you want to compare prompts, styles, and models without rewriting the transport layer each time.
A practical way to think about the call:
- Prompt — the natural-language description of the image you want.
- Style — a request-body field that steers the visual treatment of the output.
- Resolution — a request-body field that shapes the output size.
Because all three travel in the same JSON body, prompt engineering and output shaping happen in one round trip. If you are deciding whether this is the right endpoint, the answer is usually yes when you need synchronous, prompt-driven image generation and want to keep provider plumbing out of your application code.
Prerequisites and authentication
Before the first call, make sure you have:
- An HiAPI account and an API key. Keep it out of source control and load it from an environment variable.
curlavailable for a quick smoke test, and Python 3 withrequestsinstalled if you plan to use the Python example.- A prompt you actually want to render — even a rough one is enough to verify the wiring.
Authentication on HiAPI is header-based. The API key is passed in the request headers, and the same header pattern applies whether you call from curl, Python, or any other HTTP client. Model selection happens through the endpoint path, so the auth header stays constant as you move between models.
A safe local setup looks like this:
export HIAPI_API_KEY="your-key-here"
Then reference $HIAPI_API_KEY in your shell commands and os.environ["HIAPI_API_KEY"] in Python. Never hardcode the key into a script you might commit, and avoid pasting it into logs or error reporting. If a key leaks, rotate it in your account settings rather than trying to scrub it from history.
The examples below assume two things: a base URL for the HiAPI API and the model path for recraft-v4.1/text-to-image. Confirm both against your account's API reference before you wire them into a build, since the exact host and path are the one part of this guide you should copy from current documentation rather than from memory.
A working curl request
The fastest way to confirm your key, endpoint, and payload shape all work is a single curl call. The request sends a JSON body with the prompt and the shaping fields, and passes the API key in the header:
curl -X POST "$HIAPI_BASE_URL/recraft-v4.1/text-to-image" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A minimalist isometric illustration of a developer workstation, soft pastel palette, clean background",
"style": "illustration",
"resolution": "1024x1024"
}'
A few things to notice about this call:
- Endpoint path. The model name lives in the path. Swapping models later means changing that segment, not rebuilding the request.
- Body, not query string.
prompt,style, andresolutionall go in the JSON body. That keeps the URL stable and makes the payload easy to log and replay. - Header auth. The
Authorizationheader carries the key;Content-Typemust beapplication/json.
If you get a non-200 response on the first try, check in this order: the key is set in your shell, the base URL has no trailing slash duplication, and the JSON body is valid (a trailing comma is the usual culprit). Once the call succeeds, save the exact command in a scratch file — a known-good curl invocation is the reference you will compare every SDK and client change against.
The same request in Python
For real code, wrap the call in a small function so retries, logging, and model swaps have one place to live. Using requests:
import os
import requests
HIAPI_BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]
def generate_image(prompt, style="illustration", resolution="1024x1024", timeout=120):
url = f"{HIAPI_BASE_URL}/recraft-v4.1/text-to-image"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"prompt": prompt,
"style": style,
"resolution": resolution,
}
response = requests.post(url, headers=headers, json=payload, timeout=timeout)
response.raise_for_status()
return response.json()
Design notes worth keeping:
json=instead ofdata=. Passing a dict tojson=sets the content type and serializes the body for you, so the payload matches thecurlversion byte for byte.- Explicit timeout. Image generation is not instant; a generous timeout prevents a false failure on a slow but healthy request. Do not rely on the client default.
raise_for_status()first. Convert HTTP errors into exceptions before you try to read the body, so a 4xx or 5xx never gets mistaken for image data.- Environment-driven config. Base URL and key both come from the environment, which keeps the function portable across local, staging, and production.
The function returns parsed JSON. How you extract the image from that JSON is the next topic, and it is the step where most integrations go wrong.
Handling the synchronous image response
recraft-v4.1/text-to-image returns image data synchronously: the image comes back in the same HTTP response as the request, with no polling loop and no job ID to track. That simplifies the client, but it moves two responsibilities onto your code — decoding the payload and treating failures explicitly.
A robust handling path looks like this:
- Check the status before parsing. Treat anything other than 200 as an error and surface the response body, which usually explains the problem.
- Read the image field deliberately. The response JSON carries the image data; depending on the field you receive, it may be a URL or encoded image bytes. Handle both paths rather than assuming one.
- Decode defensively. If the payload is base64-encoded, decode it and write bytes to a file. Do not route binary through text-only paths or print it to a terminal.
- Bound the payload size. Synchronous image responses are large compared with typical JSON. Stream to disk where your client supports it, and avoid holding multiple responses in memory at once.
A minimal save step for a base64 payload:
import base64
data = generate_image("A calm forest at dawn, soft light")
image_b64 = data["image"] # adjust to the actual field name in the response
with open("out.png", "wb") as f:
f.write(base64.b64decode(image_b64))
Two failure modes deserve dedicated handling. Timeouts should be retried with backoff, but only for idempotent expectations — regenerating an image produces a different result, so log the prompt alongside every attempt. Client errors such as a malformed body or invalid option value will not fix themselves on retry; fail fast and log the response text. Finally, validate the image field exists before decoding, so a schema change or an error payload raises a clear exception instead of a confusing decode error.
Tuning style and resolution for your use case
style and resolution are the two request-body fields that most change what comes back, and both are cheap to iterate on because they ride along with the prompt.
Style
style steers the visual treatment of the output. Use it to keep a series of images consistent — for example, pinning one style across a batch of product illustrations or article headers so they look like a set. When results feel off, the first check is whether the style matches the content: an illustration-oriented style applied to a photorealistic prompt will fight the text. Try a simple prompt with a fixed style before adding detail, so you can tell whether a bad result came from the style or the wording.
Resolution
resolution shapes the output size. Match it to the destination: pick a square-ish size for avatars and thumbnails, and a wider size for banners and hero images. Two practical rules:
- Do not upscale what you can request. Generate near the final display size rather than generating small and scaling up.
- Keep aspect ratio in mind. If your layout expects a specific ratio, choose the resolution that matches it instead of cropping after the fact.
Debugging unexpected output
When an image looks wrong, isolate one variable at a time. Hold the prompt constant and change style; then hold both constant and change resolution. If neither helps, rewrite the prompt with concrete nouns and explicit composition cues rather than adding adjectives. Also confirm you are not silently sending the wrong value type — a style string that does not match an accepted option may be rejected or ignored depending on validation. Log the exact request body for every call; it is the only reliable record of what produced a given image.
Wrapping it for production
The value of the small function from earlier shows up when you harden it. A production wrapper should do four things:
- Encapsulate transport. One function owns the URL, headers, payload shape, and timeout. Callers pass a prompt and options, nothing else.
- Add retries with backoff. Retry timeouts and 5xx responses a bounded number of times; never retry 4xx errors. Because image generation is not idempotent, cap retries low and keep the prompt attached to each attempt.
- Log the request, not just the result. Record prompt, style, resolution, status, and duration. When someone asks why an image looks the way it does, that log is the answer.
- Make the model a parameter. Pass the model path as an argument with
recraft-v4.1/text-to-imageas the default. Swapping to another image model then becomes a config change rather than a refactor, which makes side-by-side evaluation cheap.
Keep the wrapper thin. Resist adding caching, queuing, or storage policy inside it — those belong one layer up, where they can evolve independently of the API call. A stable interface also means your tests can mock the transport and assert on the payload you would send, which catches most integration mistakes before they reach a live request.
With auth, a known-good request, and explicit response handling in place, you have everything needed to call recraft-v4.1/text-to-image from HiAPI in both curl and Python, and to swap models later without touching the rest of your code.
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