A reproducible image-to-image workflow with real before/after outputs, exact prompts, a batch script, and verified pricing
Choose a model, enter your prompt, and see the result.
HiAPI Blog
HiAPI
Generate it with HiAPI
A vacant listing photo tells a buyer less than a furnished one. Physical staging fixes that but costs real money and takes days to schedule per unit. An image-to-image API can do the same job from a single empty-room photo: keep the real room — its walls, floor, window, and camera angle — and add furniture around it.
This guide walks through that workflow end to end using the canonical model ID gpt-image-2/image-to-image, called with POST https://api.hiapi.ai/v1/tasks. It includes two real before/after pairs generated for this guide (a living room and a bedroom), their exact prompts, a reusable batch script, and verified pricing.
Text-to-image generation invents a room from a description. That is the wrong tool for staging, because the output has to match the actual unit a buyer will walk into. Image-to-image generation instead takes the empty-room photo as a reference and edits it, so the room's real geometry survives.
The request has two parts: a prompt describing only what should change (the furniture and decor to add), and an input_urls array holding the reference photo — the empty room, hosted at a public URL. The model reads the reference image, keeps the camera angle, walls, floor, and window fixed, and renders furniture into the scene as if it were placed in that exact room.
POST https://api.hiapi.ai/v1/tasks
{
"model": "gpt-image-2/image-to-image",
"input": {
"prompt": "<furniture and decor to add, plus an instruction to keep the room unchanged>",
"input_urls": ["<public URL of the empty-room photo>"],
"aspect_ratio": "16:9",
"resolution": "1K"
}
}
That single design choice — reference in, edit out — is what separates virtual staging from generic room generation. The two examples below show it on a real unvarnished photo of an empty living room and an empty bedroom.

A professionally lit real estate photograph of a completely empty living room in
a modern apartment: light oak hardwood floors, plain white walls, one large
window with soft daylight, closed white door on the right wall, ceiling with a
single flush-mount light fixture, no furniture, no people, no text or
watermarks, wide-angle real estate listing photography style, sharp focus,
natural color grading.
That first image is the reference photo, generated with gpt-image-2/text-to-image to stand in for a real listing photo of a vacant unit (aspect ratio 16:9, resolution 1K, recorded cost $0.03, generated 2026-09-23). In a production workflow this would simply be the agent's own uploaded photo — the staging step below works the same way regardless of where the empty-room photo came from.

Using the empty room in the reference photo, keep the exact room geometry,
floor, walls, window, and door unchanged, and add tasteful modern furniture to
stage it as a livable listing photo: a light gray fabric sofa with two throw
pillows, a round wooden coffee table, a neutral area rug, a floor lamp in the
corner, and a piece of abstract wall art above the sofa. Keep the camera angle
and lighting identical to the reference photo. No people, no text or
watermarks, real estate listing photography style.
Evidence: model gpt-image-2/image-to-image, input_urls set to the empty-room photo above, aspect_ratio: 16:9, resolution: 1K, recorded cost $0.03, generated 2026-09-23. The window position, the door on the right wall, and the floor and wall tone all carry over from the reference photo — only the furniture is new.

A professionally lit real estate photograph of a completely empty bedroom in a
modern apartment: light oak hardwood floors, plain white walls, one window with
sheer curtains letting in soft daylight, a closet with sliding white doors on
one wall, ceiling with a single flush-mount light fixture, no furniture, no
people, no text or watermarks, wide-angle real estate listing photography
style, sharp focus, natural color grading.

Using the empty room in the reference photo, keep the exact room geometry,
floor, walls, window, and closet doors unchanged, and add tasteful modern
furniture to stage it as a livable listing photo: a queen-size bed with a light
gray upholstered headboard and neutral bedding, two matching nightstands with
small lamps, a folded throw blanket at the foot of the bed, and a framed piece
of wall art above the headboard. Keep the camera angle and lighting identical to
the reference photo. No people, no text or watermarks, real estate listing
photography style.
Evidence: model gpt-image-2/image-to-image, input_urls set to the empty bedroom photo above, aspect_ratio: 16:9, resolution: 1K, recorded cost $0.03, generated 2026-09-23. The sliding closet doors and window placement stay put; the bed, nightstands, and art are the only additions.
Both staged outputs came from a prompt structure with three parts every time: an instruction to preserve the reference room, a specific furniture list, and a photography style tag. Dropping the preservation instruction is the most common way this goes wrong — without it, the model treats the reference more loosely and can shift the camera angle or room proportions.
A single listing usually needs more than one room staged, and a brokerage needs many listings. The script below takes a dictionary of empty-room photo URLs, submits a staging task for each one, and polls until every output is ready.
import os
import time
import requests
API_URL = "https://api.hiapi.ai/v1/tasks"
HEADERS = {
"Authorization": f"Bearer {os.environ['HIAPI_TOKEN']}",
"Content-Type": "application/json",
}
STAGING_BRIEF = (
"keep the exact room geometry, floor, walls, and window unchanged, and add "
"tasteful modern furniture to stage it as a livable listing photo, real "
"estate listing photography style, no people, no text or watermarks"
)
# name -> public URL of the empty-room photo
ROOMS = {
"unit-204-living-room": "https://your-storage.example.com/unit-204-living-room.jpg",
"unit-204-bedroom": "https://your-storage.example.com/unit-204-bedroom.jpg",
}
def submit_staging_job(empty_room_url, aspect_ratio="16:9", resolution="1K"):
response = requests.post(
API_URL,
headers=HEADERS,
json={
"model": "gpt-image-2/image-to-image",
"input": {
"prompt": STAGING_BRIEF,
"input_urls": [empty_room_url],
"aspect_ratio": aspect_ratio,
"resolution": resolution,
},
},
timeout=60,
)
response.raise_for_status()
return response.json()["data"]["taskId"]
def wait_for_output(task_id, timeout_s=300, poll_interval=5):
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API_URL}/{task_id}", headers=HEADERS, timeout=30)
r.raise_for_status()
task = r.json()["data"]
if task["status"] == "success":
return task["output"][0]["url"] # expires — download immediately
if task["status"] == "fail":
raise RuntimeError(task["error"])
time.sleep(poll_interval)
raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")
task_ids = {name: submit_staging_job(url) for name, url in ROOMS.items()}
staged_urls = {name: wait_for_output(tid) for name, tid in task_ids.items()}
print(staged_urls)
Two details matter for a real batch. First, input_urls takes one reference photo per task — stage one room per call rather than trying to combine several rooms into a single request. Second, each output[0].url in the response is a time-limited link: download the bytes and store them in your own bucket right after the task succeeds, the same way this guide immediately uploaded its own outputs to permanent storage.
gpt-image-2/image-to-image is priced by output resolution, confirmed against HiAPI's live pricing endpoint on 2026-09-23:
| Resolution | Price per staged image |
|---|---|
| 1K | $0.03 |
| 2K | $0.04 |
| 4K | $0.06 |
The planning formula is:
batch cost = units × rooms staged per unit × price per image
Using the 1K price from the table:
| Listing batch | Rooms per unit | Staged images | Batch cost |
|---|---|---|---|
| 1 unit | 3 | 3 | $0.09 |
| 10 units | 3 | 30 | $0.90 |
| 50 units | 4 | 200 | $6.00 |
| 200 units | 4 | 800 | $24.00 |
Prices change, so check the live pricing page before committing to a large batch, and add a regeneration allowance based on your own review pass — the two examples in this guide are not a claim about a universal first-pass approval rate.
Image-to-image staging edits pixels, not the physical unit. Keep three limits in mind before shipping it into a listing workflow:
Use gpt-image-2/image-to-image with POST /v1/tasks, passing the empty-room photo's public URL in input_urls and a prompt that names the furniture to add. See the model documentation for the full input schema.
It should, but verify it. The prompt structure in this guide — preserve the reference, then list only the furniture to add — kept the window, doors, floor, and wall tone consistent in both examples here. Still, review each output against its source photo before publishing, since generation quality can vary by room and lighting.
At the 1K price of $0.03 per image, staging three rooms in one unit costs $0.09. Multiply by rooms-per-unit and units-in-batch to plan a larger run, and confirm the current price first since resolution and future pricing changes affect the total.
Check the rules for your market. Many MLS systems and local regulations require a disclosure label on virtually staged photos, separate from any general accuracy requirement for listing images.
Yes — preserving a reference image while changing a described element is a general pattern. The GPT Image 2 e-commerce workflow uses the same underlying task API for product photography batches, with a comparable prompt and pricing structure.
Start with one vacant-unit photo on the GPT Image 2 image-to-image model page, confirm the staged room matches the real unit, then move the same input_urls call into the batch script above for the rest of the listing.