Generate, then refine an image
Make an image under a price cap, feed it back as a reference to refine it, and have a model compare the versions, all on one Flux key.
First drafts are rarely right. This recipe makes an image, then refines it in rounds: each round sends the last result back as a reference image with a short change, and a vision model compares the versions so your code can stop when one is good enough. Every call carries max_price, so no round can cost more than you allow.
| Step | Status |
|---|---|
| Generate | Live |
| Edit with a reference image | Live |
Images are a paid ability; you need a paid, cleared plan. Edits run from your own code: the tools server has no edit tool.
1. Generate under a price cap
Use a model that can edit too, so every round stays on one look. flux-image (the default) and flux-image-pro can both edit.
import base64
import requests
BASE = "https://api.fluxrouter.ai/v1"
HEADERS = {"Authorization": f"Bearer {FLUX_API_KEY}"}
MODEL = "flux-image"
MAX_PRICE = 0.25 # your cap per call, in US dollars
resp = requests.post(
f"{BASE}/images/generations",
headers=HEADERS,
json={
"model": MODEL,
"prompt": "A flat illustration of a lighthouse at dusk, teal and orange",
"max_price": MAX_PRICE,
},
)
resp.raise_for_status()
png = base64.b64decode(resp.json()["data"][0]["b64_json"])
with open("v1.png", "wb") as f:
f.write(png)
If the call would cost more than max_price, it answers 402 price_exceeds_max_price, with no image and no charge. Raise the cap or pick a cheaper model.
2. Refine with the result as a reference
Send the image back to POST /v1/images/edits with one short change. Small, specific changes work better than a rewritten prompt.
def refine(png: bytes, change: str) -> bytes:
resp = requests.post(
f"{BASE}/images/edits",
headers=HEADERS,
data={"model": MODEL, "prompt": change, "max_price": str(MAX_PRICE)},
files=[("image[]", ("current.png", png, "image/png"))],
)
resp.raise_for_status()
body = resp.json()
print("This edit cost (USD):", body.get("usage", {}).get("cost"))
return base64.b64decode(body["data"][0]["b64_json"])
v2 = refine(png, "Add a small sailing boat on the horizon. Keep everything else the same.")
with open("v2.png", "wb") as f:
f.write(v2)
You can send up to 4 reference images in one edit, for example the current draft plus a style sample. Each file can be up to 10 MB.
3. Compare the versions
Ask a vision model which version better matches the brief. Both images go in one chat message, as data URLs.
def data_url(png: bytes) -> str:
return "data:image/png;base64," + base64.b64encode(png).decode()
def better(brief: str, a: bytes, b: bytes) -> str:
resp = requests.post(
f"{BASE}/chat/completions",
headers=HEADERS,
json={
"model": "flux-auto",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": f"Brief: {brief}\nImage A, then image B. "
"Which matches the brief better? Answer A or B, then one sentence why."},
{"type": "image_url", "image_url": {"url": data_url(a)}},
{"type": "image_url", "image_url": {"url": data_url(b)}},
],
}],
},
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
print(better("A lighthouse at dusk with a boat on the horizon", png, v2))
See Vision and image inputs for the message shape.
Put it in a loop
Run a fixed list of changes, keep the better version each round, and stop early when the model is happy. Cap the rounds: each one is a billed edit.
changes = [
"Add a small sailing boat on the horizon. Keep everything else the same.",
"Make the lighthouse beam brighter. Keep everything else the same.",
]
best = png
for change in changes[:3]: # never more than three rounds
candidate = refine(best, change)
verdict = better("A lighthouse at dusk, a boat on the horizon, a bright beam", best, candidate)
if verdict.strip().startswith("B"):
best = candidate
with open("final.png", "wb") as f:
f.write(best)
What it costs
- Each generation and each edit is billed per image: from $0.01 per image.
max_pricecaps each call. - Each comparison is a normal chat request, billed by tokens.
- A call refused for
max_price, a blocked prompt or a failure is not charged.
The whole run is itemised on your usage page. For unattended loops, set a spend cap on the key; see Spend caps.
If something goes wrong
See Image problems, including edit_unsupported (pick flux-image or flux-image-pro) and invalid_reference_image (check the file type and size). The full contract is in the images reference.