Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
Restyling a photo with AI — turning a snapshot into an anime frame, a studio product shot, or a watercolor illustration — is not a special "style transfer" endpoint. It's a regular image-to-image call: you send a source image and a text instruction that names the destination style, and the model returns a new image. The part that actually determines whether you get a clean restyle or a half-regenerated photo is how you write that instruction, not a parameter you tune. This guide covers the request mechanics on hiapi's unified tasks API, the prompt pattern that keeps a restyle from drifting into a new image, and how to pick between two verified image-to-image models based on how much creative latitude you want.
Authorization: Bearer sk-... header on every request.localhost paths or private buckets without a signed URL won't work. A presigned S3/R2/GCS URL is fine.Every model id and field below was re-verified against the live API before publishing, including a forced-error probe to confirm the current enum values.
Neither of the models in this guide exposes a numeric strength or intensity parameter — sending one (strength: 0.5) is rejected outright with additional properties 'strength' not allowed. Style strength is controlled entirely by prompt wording, which means an instruction like "make this a Studio Ghibli anime frame" alone tells the model what to add but not what to leave alone — subject identity, pose, and composition are fair game for the model to reinterpret.
The fix is to always write two clauses into the prompt:
A prompt like "Restyle this photo as a woodblock print. Keep the subject's pose, framing, and the position of every object in the scene." gives the model a target and a constraint in the same instruction, and is the single biggest lever for getting a restyle instead of a re-generation.
This example uses seedream-5.0-pro/image-to-image, which restyles well and has a strict, fully-verified schema:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-5.0-pro/image-to-image",
"input": {
"prompt": "Restyle this portrait as a watercolor painting with visible brush texture and soft bleeding edges. Keep the subject'\''s pose, expression, and framing exactly as in the source.",
"image_urls": ["https://your-bucket.example.com/portrait.jpg"],
"aspect_ratio": "3:4",
"resolution": "2K"
}
}'
aspect_ratio is required and comes from a fixed enum — currently 1:1, 4:3, 3:4, 16:9, 9:16, 2:3, 3:2, 21:9. There's no "match source" option on this endpoint, so pick the value closest to your source image's real ratio (a portrait photo → 3:4 or 9:16, not 16:9) to avoid the model padding or cropping the composition to fit.
A successful create returns the task id at data.taskId:
{ "data": { "taskId": "<your-task-id>" } }
Poll until it reaches a terminal state:
curl -s https://api.hiapi.ai/v1/tasks/YOUR_TASK_ID \
-H "Authorization: Bearer sk-YOUR_KEY"
data.status ends at success (output at data.output[0].url) or fail (details in data.error).
Standard library only:
import json
import time
import urllib.request
API = "https://api.hiapi.ai/v1/tasks"
KEY = "sk-YOUR_KEY"
def call(url, payload=None):
req = urllib.request.Request(
url,
data=json.dumps(payload).encode() if payload is not None else None,
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
},
method="POST" if payload is not None else "GET",
)
with urllib.request.urlopen(req) as resp:
return json.load(resp)
# 1. Create the restyle task
create = call(API, {
"model": "seedream-5.0-pro/image-to-image",
"input": {
"prompt": (
"Restyle this street photo as a 1980s film-grain photograph with warm, "
"faded colors. Keep the same subject, pose, and every object in the "
"same position in the frame."
),
"image_urls": ["https://your-bucket.example.com/street.jpg"],
"aspect_ratio": "16:9",
"resolution": "2K",
},
})
task_id = create["data"]["taskId"]
print("task created:", task_id)
# 2. Poll until terminal state
while True:
task = call(f"{API}/{task_id}")["data"]
if task["status"] == "success":
break
if task["status"] == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(5)
# 3. Download immediately -- output URLs expire
image_url = task["output"][0]["url"]
urllib.request.urlretrieve(image_url, "restyled.png")
print("saved restyled.png")
Both models were re-verified live and neither accepts a strength parameter, but they differ in how much control you have over the source image's influence:
seedream-5.0-pro/image-to-image | grok-imagine-quality/image-to-image | |
|---|---|---|
| Reference images | 1–10 public URLs | 1–3 public URLs |
resolution | 1K / 2K (uppercase) | 1k / 2k (lowercase) — optional |
| Framing control | Fixed 8-value aspect_ratio enum, no auto-match | aspect_ratio: "auto" follows the input frame |
| Best for | Precise, single-reference restyles where you already know the target ratio | Quick restyles where you want the output to inherit the source's exact framing without picking a ratio yourself |
If your keep clause includes "same framing" and you don't want to compute the source's aspect ratio by hand, grok-imagine-quality/image-to-image with aspect_ratio: "auto" is the more direct path — swap it into the request above with image_urls capped at 3 and resolution set to lowercase 2k.
Prefer a callback over polling on servers. Add a callback object at request time and hiapi POSTs once, at the terminal state, instead of you polling:
{
"model": "seedream-5.0-pro/image-to-image",
"input": { "...": "..." },
"callback": { "url": "https://your-server.example.com/hiapi-hook", "when": "final" }
}
Treat the callback payload as a signal, not a source of truth — take the task id from it and re-fetch GET /v1/tasks/<id> yourself before acting. For scripts and notebooks, polling every 5 seconds is fine.
Make retries idempotent. Store your own mapping of source image → taskId when you create a task. If your process crashes mid-poll, re-attach to the stored taskId on restart instead of resubmitting — a resubmit is a second billable task.
Errors you'll actually see:
401 permission_denied — malformed or missing key. The header must be exactly Authorization: Bearer sk-....400 INVALID_REQUEST — schema violations, e.g. aspect_ratio: value must be one of '1:1', '4:3', ... or additional properties 'strength' not allowed. The schema is strict on both models — don't carry parameters across model families.image_urls must be an array — a bare string was passed instead of a list, even for a single reference image.Is there a strength or intensity slider for style transfer?
No. Neither model in this guide accepts a strength field — sending one is rejected as an unrecognized property. Style intensity is controlled entirely through how specific and forceful your prompt's style description is.
How do I stop the model from changing my subject's pose or identity? Add an explicit keep clause to the prompt naming what must stay fixed — pose, expression, framing, object positions. Models restyle more conservatively when the constraint is spelled out than when it's implied.
Can I preserve my source image's exact aspect ratio automatically?
On grok-imagine-quality/image-to-image, yes — set aspect_ratio: "auto". On seedream-5.0-pro/image-to-image, no auto option exists; pick the closest value from its fixed enum to your source image's real ratio.
Do my source images have to be public URLs?
They must be fetchable by hiapi's servers over HTTPS. Presigned URLs from a private bucket work; localhost or unauthenticated-but-unreachable paths don't.
Can I generate several style variations from one photo in parallel?
Yes — each variation is an independent task. Fire off multiple POST /v1/tasks calls with the same image_urls and a different style instruction, then poll or callback on each taskId separately.
What's the difference between restyling and inpainting or object removal? Restyling changes the rendering of the whole image while keeping content fixed; inpainting/object removal changes specific regions while keeping the rendering style fixed. They're different prompt patterns on the same kind of image-to-image endpoint — see the inpainting and object removal guide if that's what you actually need.