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.

StepStatus
GenerateLive
Edit with a reference imageLive

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.

python
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.

python
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.

python
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.

python
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_price caps 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.