UrsaKit cover for the guide to calling a FLUX 3 image API and optimizing the generated files

FLUX 3 Image is served through a hosted API rather than something you run on a laptop, which means the interesting work happens after the request returns. The model hands back a fixed high-resolution image with no awareness of your page budget, so the difference between a fast site and a slow one is the pipeline you wrap around the call.

This guide covers the request and response shape most image APIs use, then the resize-and-encode step that turns the result into something you can actually ship.

01What a hosted image API changes about your pipeline

A local model returns a tensor or a file on disk. A hosted one returns either a URL or a base64 blob inside JSON, and each choice pushes cost to a different place. A URL keeps the API response small but usually expires within hours and forces you to download and re-host the bytes anyway. Base64 keeps everything in one response but inflates the transfer by about 33% and makes your request logs enormous.

Verify field names against the current docs

The request below follows the shape most image APIs use — a JSON body with a prompt and size, authenticated with a bearer token. Black Forest Labs publishes the exact FLUX 3 endpoint and parameter names, and those change; treat this as a template, not a frozen contract.

02Request an image from the API

Send a POST with a JSON body and an authorization header. Model and endpoint names differ between providers, so keep them in configuration rather than hard-coding them in ten places.

curl -X POST "$IMAGE_API_URL" \
  -H "Authorization: Bearer $IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "isometric illustration of a filing cabinet, soft indigo palette",
    "width": 1024,
    "height": 1024,
    "format": "png"
  }'

Respect the aspect ratio you actually need. Generating a square and cropping to a banner throws away pixels you paid for, and asking for a very wide canvas wastes tokens on regions you will trim.

03Handle the response: URL or base64

The two response shapes need different code paths. The URL form is a short JSON body you fetch a second time; the base64 form is a large string you decode and write to disk.

{
  "id": "req_9f3c1a",
  "status": "ready",
  "image": {
    "url": "https://cdn.example.invalid/generations/req_9f3c1a.png",
    "expires_at": "2026-10-04T12:00:00Z"
  },
  "seed": 812347
}
const res = await fetch(process.env.IMAGE_API_URL, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.IMAGE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ prompt, width: 1024, height: 1024, format: 'png' }),
})

const data = await res.json()
const bytes = data.image.url
  ? Buffer.from(await (await fetch(data.image.url)).arrayBuffer())
  : Buffer.from(data.image.b64, 'base64')

await fs.writeFile(`out/${data.id}.png`, bytes)

Persist the seed alongside the file. It is the only way to reproduce or riff on a result later, and regenerating from scratch is rarely identical.

04Resize before you compress

Order matters. If you compress first and resize second, the resize has to invent or discard pixels that the lossy encode already damaged. Resize to the display size first, then apply one lossy encode.

Display targetStoreNotes
600 px card1200 pxCovers 2× displays
1200 px hero2000 pxCap around 2000 px; beyond that, gains are invisible
Full-bleed background2560 pxOnly if the layout truly spans the viewport

For a typical blog or product card, a 1024-pixel API result is already larger than you need. Downscale to roughly twice the rendered width and stop.

05Pick the format and encode once

Generated images are usually PNGs, which are lossless and therefore heavy. For photographic output, convert to WebP; it supports transparency, so you do not lose alpha by switching. Choose AVIF only if you can serve a fallback, because encoder support and decode cost still vary across older devices.

# One lossy pass from the original, after resizing.
cwebp -q 82 -m 6 -resize 1200 0 source.png -o output.webp

Tune quality visually rather than by number. A quality setting is not a file-size percentage: the same value can halve one image and barely move another, depending on how much high-frequency detail the model produced.

Never re-encode an already-lossy file to fix its size

Running a JPEG through the encoder a second time to shrink it discards more detail at the same quality setting. Always keep the original PNG, adjust your parameters, and re-encode from that master.

06Wire it into a build or CMS

Run the pipeline once at ingest, not on every request. Generate the image, write the original to object storage, then produce two or three derivatives with fixed widths and serve whichever matches the viewport.

The stable set for most sites is 400, 800 and 1600 pixels wide, exposed through srcset. Store the seed, prompt and model name in your database next to the asset so you can audit where an image came from. If a page renders a generated image at full width without a width and height attribute, you also get layout shift on top of the weight problem.

07Common errors and fixes

SymptomCauseFix
Request returns 401Missing or wrong bearer tokenCheck the header is sent, not just stored
Response is enormous JSONBase64 image inline in the payloadRequest a URL, or decode to disk before logging
URL stops working after a daySigned link expiredDownload and re-host immediately
Output looks blurryUpscaled beyond the generated resolutionGenerate at the target size instead of enlarging
Transparency turned blackConverted a transparent PNG to JPEGKeep WebP or PNG for images with alpha

The rule of thumb: treat the API output as a master asset, never as the file you serve. Downscale once, encode once, and keep the untouched original so you can change your mind without asking the model for the same image twice.

Frequently asked questions

Why is the image my API returned so large?

Image models return a fixed high resolution, often 1024 pixels or more on the long edge, with no web-oriented encoding. Nothing in the API knows or cares about your page budget.

Should I request a URL or a base64 response?

A URL is smaller and easier to cache, but it expires and adds a second request. Base64 is self-contained but inflates the payload by roughly a third and bloats JSON logs.

Does converting PNG to WebP lose transparency?

No. WebP supports an alpha channel, so transparent PNG output converts cleanly. Only convert to JPEG when the image has no transparency and is photographic.

Why does re-compressing an image make it look worse each time?

Every lossy encode discards detail that the next encode cannot recover. Compress once from the highest-quality source you have instead of chaining lossy steps.

What resolution should I store for a website?

Store at the largest size you actually display, then generate smaller variants. Storing a 2048-pixel master for a 600-pixel card wastes bytes on every request.

Back to articles

Related articles

Related tools