HiAPI
Blog

Tutorial

How to Use recraft-v4.1/text-to-image via the HiAPI API: curl and Python Guide

Nov 3, 2026 10 min read
深炭色背景上一条宽大的琥珀色光带斜向掠过,边缘点缀少量灰褐色几何块面,为教程类文章的封面图。

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.
  • curl available for a quick smoke test, and Python 3 with requests installed 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, and resolution all go in the JSON body. That keeps the URL stable and makes the payload easy to log and replay.
  • Header auth. The Authorization header carries the key; Content-Type must be application/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 of data=. Passing a dict to json= sets the content type and serializes the body for you, so the payload matches the curl version 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:

  1. Check the status before parsing. Treat anything other than 200 as an error and surface the response body, which usually explains the problem.
  2. 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.
  3. 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.
  4. 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-image as 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 →

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