Where to Run FLUX Image Models via API
Around fifty FLUX endpoints, one request shape. Which variant does what, the parameters that change your output, and how to debug a bad render.

You've decided on FLUX. Good, that part's easy. Now pick the variant, and suddenly there are four generations, six size tiers, edit siblings for most of them, a LoRA path, and a handful of structural-control models that look nothing like a text-to-image call. Choosing wrong doesn't produce an error. It produces an image that's fine, at a latency you didn't need to accept.
You can run FLUX image models through the Eachlabs API, currently around fifty FLUX endpoints across the family, all reachable with one key and one request shape. This page covers which variant does what, the exact call, the parameters that actually change your output, the limits worth knowing before you build, and how to debug a render that came back wrong.

What the FLUX family covers, and which parts matter to you
FLUX is a family of diffusion transformer image models from Black Forest Labs. Four things are worth internalizing before you pick.
Generation. FLUX.1 is the original line. FLUX.2 is the current workhorse for text-to-image and image editing. FLUX.3 is video (text-to-video, image-to-video, timestamped image-to-video, and video continuation), which is a different job entirely and the reason a search for "FLUX image models" turns up video endpoints.
Capability tier. Inside FLUX.2 the variants sit on a speed-to-fidelity ladder: flux-2-max-text-to-image at the top, then flux-2-pro, flux-2 and flux-2-flex in the middle, flux-2-turbo-text-to-image and flux-2-flash-text-to-image tuned for throughput, and the distilled flux-2-klein-4b and flux-2-klein-9b variants at the light end. FLUX.1 keeps flux-1-1-pro, flux-1-1-pro-ultra, flux-dev and the very fast flux-schnell.
Task shape. Almost every FLUX.2 text-to-image endpoint has an -edit sibling that takes an existing image instead of only a prompt: flux-2-max-edit, flux-2-turbo-edit, flux-2-flash-edit, flux-2-flex-edit, flux-2-pro-edit, and the klein edit variants. Separately there's the Kontext line: flux-kontext-pro, flux-kontext-max, flux-kontext-dev, plus multi-image-kontext, multi-image-kontext-pro and multi-image-kontext-max for edits driven by several reference images at once.
Structural control and utilities. This is the part people miss. flux-canny-pro conditions on edges. flux-depth-pro and flux-depth-dev condition on depth. flux-fill-pro does inpainting and outpainting. flux-redux-dev does image variation. flux-krea-image-to-image handles stylistic image-to-image. flux-vision-upscaler upscales. And on the training side, flux-lora-portrait-trainer produces a LoRA you then run through flux-2-lora, flux-2-lora-edit, flux-hf-lora, flux-kontext-lora-text-to-image or bfl-flux-lora. There's also flux-dev-realism for photographic output and the SRPO-tuned tencent-flux-1-srpo-text-to-image and tencent-flux-1-srpo-image-to-image.
If you want the short version: start at flux-2-flash-text-to-image for volume, flux-2-max-text-to-image for hero assets, flux-kontext-pro when you're editing something that already exists, and flux-fill-pro when you need to change part of an image rather than all of it.

Quick start: one POST, one poll
Every model on the platform, FLUX included, uses the same envelope. A slug, a version, and an input object:
curl -X POST https://api.eachlabs.ai/v1/prediction/ \
-H "Authorization: Bearer $EACHLABS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2-max-text-to-image",
"version": "0.0.1",
"input": {
"prompt": "An overhead studio shot of a brass desk lamp on grey linen, single soft key light from the left, shallow depth of field",
"image_size": "landscape_16_9",
"num_images": 1,
"enable_prompt_expansion": false
},
"webhook_url": ""
}'The response hands back a prediction ID and nothing else, no image:
{
"status": "success",
"message": "Prediction created successfully",
"predictionID": "03781b27-2c4a-411a-a9e8-cfd2d6726773"
}
Then poll GET /v1/prediction/{id}. Predictions walk created → starting → processing and settle on success, error, or cancelled. Three to five seconds between polls is the right cadence.
import os, time, requests
BASE = "https://api.eachlabs.ai/v1/prediction/"
H = {"Authorization": f"Bearer {os.environ['EACHLABS_API_KEY']}"}
TERMINAL = {"success", "error", "cancelled"}
def run(model: str, payload: dict, version: str = "0.0.1"):
r = requests.post(BASE, headers=H, timeout=30, json={
"model": model, "version": version,
"input": payload, "webhook_url": "",
})
r.raise_for_status()
pid = r.json()["predictionID"]
while True:
d = requests.get(BASE + pid, headers=H, timeout=30).json()
if d["status"] in TERMINAL:
break
time.sleep(4)
if d["status"] != "success":
raise RuntimeError(f"{pid} ended {d['status']}: {d.get('output')}")
return d["output"], d["metrics"]["predict_time"]
url, took = run("flux-2-flash-text-to-image", {
"prompt": "A weathered wooden dinghy on wet sand at dawn",
"image_size": "landscape_4_3",
"guidance_scale": 2.5,
})
On success, output holds the image URL (or an array of them when num_images is above one) and metrics.predict_time reports the real processing time. That's the number to build your batch windows on, not a figure from a blog post.
To swap variants, change the slug. Nothing else in that function moves. That's the whole argument for calling FLUX this way rather than integrating per-model.
The parameters that change your output
FLUX.2 and FLUX.1 expose meaningfully different controls, and copying a payload across generations is the fastest way to a 400.
Sizing splits by generation. The FLUX.2 text-to-image and edit endpoints take image_size as a preset: square_hd, square, portrait_4_3, portrait_16_9, landscape_4_3, landscape_16_9. Generation defaults to landscape_4_3 and edits to square_hd. FLUX.1 models instead take a free-form aspect_ratio string: flux-schnell accepts eleven values from 1:1 through 21:9 and 9:21. Kontext takes aspect_ratio too, and its default is match_input_image, which preserves your source dimensions, usually what you want for an edit, and the reason a Kontext output sometimes isn't the shape you expected.
guidance_scale is the prompt-adherence dial. On FLUX.2 it runs 0 to 20 and defaults to 2.5. Kontext defaults to 3.5. Higher follows the prompt harder and starts producing stiff, over-saturated results past the middle of the range; lower drifts. If your outputs feel generically pretty and ignore half your prompt, raise it a point. If they look scorched, drop it.
Prompt expansion is a trap if you don't know it's on. enable_prompt_expansion defaults to true on flux-2-max-text-to-image and false on flux-2-flash-text-to-image. When it's on, the model rewrites your prompt before rendering, which helps short vague prompts and actively fights carefully engineered ones. If you've tuned your prompts, turn it off explicitly on every endpoint rather than relying on defaults. Kontext has the same idea under a different name: enhance_prompt, defaulting to false.
Acceleration trades quality for speed. Several endpoints, including flux-2-max-edit, take acceleration as none, regular or high, defaulting to none. flux-schnell exposes the same idea as go_fast, which defaults to true and runs an fp8-quantized path. Disable it to get original bf16 at the cost of latency. flux-schnell also takes megapixels as 1 or 0.25, which is the simplest throughput lever in the family.
Reproducibility. Most endpoints accept a seed. Set it if you ever need to explain why an asset looks the way it does, or to isolate whether a change came from your prompt or from sampling noise. Store the seed next to the asset.
Safety. enable_safety_checker defaults to true on FLUX.2; some FLUX.1 endpoints expose the inverse as disable_safety_checker. Several also take a numeric safety_tolerance. Read the schema rather than assuming the polarity: GET /v1/model?slug=flux-2-max-edit returns the full request schema for any endpoint, and it's the authoritative answer.
Upscaling has its own controls. flux-vision-upscaler takes an image_url, a creativity value from 0 to 1 defaulting to 0.3, and a guidance value from 0 to 5 defaulting to 1. Creativity above roughly 0.5 invents detail rather than recovering it, which is wonderful for concept art and wrong for a product photo.

Compatibility, inputs, and limits
Edit and control endpoints take images by public URL, not as multipart uploads. If your source is private, push it through each::storage and pass the resulting URL. The multi-image Kontext endpoints take several references at once; single-image endpoints take one.
Latency varies by an order of magnitude across the family, and that's the point of the family existing. Rather than trusting a number here, run your own prompt through three tiers once and read metrics.predict_time off each. That takes ten minutes and answers the question permanently for your workload.
On concurrency: there's no per-second request limit on predictions, but there is an account concurrency cap, and exceeding it returns 429 with a details field naming the cap that applied. A rejected request creates no prediction, so it costs you nothing and needs a resubmit rather than a failure path.
For anything long-running or user-facing, pass a webhook_url instead of polling. Webhook payloads use a compact succeeded/failed vocabulary rather than the six polling states, and the same webhook may be delivered more than once. So keep the two status parsers separate and make the handler idempotent on the prediction ID.
Now the part a docs page usually skips. FLUX is strong and it is not uniform. Small text inside an image is still unreliable across the whole family; if you need legible words, composite them yourself rather than prompting for them. Hands and complex mechanical assemblies still fail at a measurable rate, which means a production pipeline needs a rejection path, not just a success path. The distilled and accelerated variants genuinely lose fine detail, that's the trade, not a bug, and it shows up first in fabric texture and hair. And prompt behaviour is not portable between generations: a prompt tuned on FLUX.1 will not land identically on FLUX.2, so budget re-tuning time when you migrate rather than assuming a slug swap is free.

Troubleshooting
The request 400s. Validation happens before any provider work, so nothing was spent. Nearly always it's a parameter that belongs to a different generation: image_size sent to a FLUX.1 endpoint that wants aspect_ratio, a preset string that isn't in the enum, or a guidance_scale outside 0 to 20. Pull the schema with GET /v1/model?slug=<slug> and diff it against your payload.
The model doesn't seem to exist. A 404 with "model not found" means the slug is wrong, usually a plausible-looking guess rather than a real endpoint. Slugs are not predictable from the marketing name. List what's actually available with GET /v1/models, which is public and doesn't need a key, or browse the text-to-image and image-to-image catalogs.
The prediction errored. Check output on the terminal response, which carries the error string, and logs if present. A 402 means the account can't fund the run; a 401 means the Bearer header is malformed, which is very often a trailing newline from a shell variable.
The output is disappointing rather than broken. Work in this order. Turn off prompt expansion so you're actually testing your prompt. Pin a seed so you're comparing like with like. Then move guidance_scale one point at a time. Only after that, change variant. And when you do, go up a tier before you go across a generation. Most "the model is bad" conclusions are a prompt being silently rewritten and an unpinned seed making every comparison meaningless.
Related
The full request and response contract, including every prediction status and the webhook payload shape, is in the prediction API reference. For chaining FLUX behind or ahead of other steps (generate, then upscale, then animate), use a workflow with the version pinned in the path. Your own source images go through each::storage. And for FLUX.3's video endpoints, start from the image-to-video catalog.