Skip to content

DocsModalities

Image generation

Generate and edit images with POST /v1/images/generations and /v1/images/edits — parameters, sizes, response shape, and per-image billing.


INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.

Image generation is synchronous. You post a prompt, the request stays open while a provider renders, and the response carries the finished images. Expect a few seconds on fast models and up to a minute on the highest-quality ones.

Two endpoints share one request shape: /v1/images/generations creates images from text, /v1/images/edits modifies an image you upload. Both accept the universal fallbacks, logging, and metadata extensions, both name the provider and connection that served the request, and both report estimated provider cost in usage.cost. Image requests are served by connections to OpenAI, Azure OpenAI, Google AI (Gemini), Google Vertex AI, Together AI, OpenRouter, Runway, KIE, LaoZhang API, fal.ai, Replicate and OpenAI-compatible endpoints; INFRO translates the request to each provider's own API. Video works differently — it is asynchronous, see Video generation.

Endpoints
POST https://api.infro.io/v1/images/generations
POST https://api.infro.io/v1/images/edits

Request body

modelstringrequired
Image model ID in vendor/model-name form, e.g. bfl/flux-2-pro, ideogram/ideogram-3, or google/nano-banana-pro, or the slug of one of your routes. Browse IDs and vendor list rates in the catalog. The model must be served by one of your connected providers.
promptstringrequired
What to render, up to 32,000 characters. Prompt conventions differ by model — recraft/recraft-v3 responds to style keywords, ideogram/ideogram-3 is the strongest at rendering text inside the image. The provider may enforce a shorter limit.
ninteger
How many images to request, 1–10. Defaults to 1. Sent to the provider as-is; a provider may return fewer, and usage.images counts what came back.
sizestring
Exact pixels, WIDTHxHEIGHT, e.g. 1024x1024. Passed to the provider unchanged; omit it and the provider's default applies. Send size or resolution and aspect_ratio, not both. See the note on sizes below.
resolutionstring
A resolution tier: 512, 768, 1K, 1.5K, 2K, 3K or 4K. Each model takes its own tiers on each provider (Nano Banana 2 Lite renders 1K only; Nano Banana Pro takes 1K, 2K and 4K), and INFRO sends the tier the way that provider spells it, as pixels where the provider takes pixels. A tier the model cannot render on a target is refused before that target is called; see below.
aspect_ratiostring
W:H, e.g. 16:9 or 9:16. Checked against what the model takes on each target, like resolution.
qualitystring
low, medium, high or auto; GPT Image 2.5 also takes xhigh and max. The legacy standard reads as medium. Sent only where the model has a quality setting (GPT Image, Grok Imagine, Ideogram's rendering speed) and dropped elsewhere; a value outside the model's own list is refused (Grok Imagine takes low or medium).
response_formatstring
url (default) returns a signed link to a copy INFRO stores for 24 hours; b64_json returns the image inline as base64. Base64 payloads are several megabytes each — prefer url unless your runtime cannot make a second fetch.
seedinteger
Best-effort deterministic sampling, forwarded on /generations only, and only to providers whose model takes a seed; elsewhere it is dropped.

Settings each model takes

The same model takes different settings on different providers, so INFRO keeps a record of what each model accepts on each provider it serves, read from that provider's own documentation. Before calling a target, the gateway checks resolution, aspect_ratio and quality against that record. A setting the target cannot take is never sent: the next target in the route is tried, and when no target can take it the request is a 400 invalid_request_error naming what the model accepts, for example Nano Banana 2 Lite on LaoZhang API renders at 1K, not 2K. No provider is called and nothing is charged. A model with no record is passed through unchanged. The console's Playground offers only these settings.

Reference images

references is a list of images that guide the render, each { "source": "https://… or data:image/…", "role": "character" | "style" | "object" | "subject" } (role defaults to subject). INFRO fetches each image once (URLs pass the same outbound checks as every re-host; SVG and HTML are refused), then checks the count and roles against what the model takes on each target — Nano Banana Pro takes 14 (6 objects, 5 characters, 3 styles), GPT Image 16, FLUX.2 [pro] on fal 9 — and sends them in that provider's own field. A target that cannot take them is skipped; when none can, the request is a 400 naming what the model takes. /v1/images/edits also takes several input images (image[], up to 16, the first is the one edited); where edit images and references share one list on the provider, their total counts against it.

Upscale an image

POST /v1/images/upscale takes one image (multipart/form-data: image, model, and scale where the model takes a factor) and returns it larger, in the same response shape as generations. Each model takes its own factors on each provider (Topaz on fal: 2, 3 or 4; Magnific on Runway: 2, 4, 8 or 16; Recraft Crisp: none, it picks); a factor a target cannot take is refused before any call, naming what it takes. Priced per image.

INFRO extensions

The same top-level extensions available on chat completions apply here, with identical semantics.

routingobject
Legacy routing fields do not rank or region-constrain customer-owned connections. Configure a named route for provider selection; see Routing.
fallbacksarray
Up to five model IDs to try, in order, if every connection for model fails, e.g. ["bfl/flux-2-dev", "qwen/qwen-image-3"]. A fallback must be an image model too. An ID that is not a model this organization can call is refused with 400 unknown_fallback_model. See Failover & fallbacks.
loggingboolean
Set false to keep this request's prompt out of payload capture when your organization has it turned on. Rendered images are never captured. Request metadata (model, provider, status, estimated cost) is still recorded. See Privacy & data.

Example request

curl https://api.infro.io/v1/images/generations \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bfl/flux-2-pro",
    "prompt": "An isometric cutaway of a small data center, soft studio lighting",
    "size": "1024x1024",
    "n": 1,
    "fallbacks": ["ideogram/ideogram-3"]
  }'

Response

json
{
  "id": "req_5c3f9a21",
  "created": 1755950000,
  "model": "bfl/flux-2-pro",
  "route": "primary",
  "route_name": null,
  "provider": "together",
  "connection": "conn_8b21c4e0",
  "data": [
    {
      "url": "https://api.infro.io/media/img/2026-09-24/org_.../req_5c3f9a21-0.png?exp=1756036400&sig=...",
      "b64_json": null
    }
  ],
  "usage": {
    "images": 1,
    "cost": 0.03,
    "price_source": "list"
  }
}
data[].urlstring
A signed link to a copy of the image INFRO stores, or null when response_format is b64_json. The link stops working 24 hours after the request.
data[].b64_jsonstring
Base64-encoded image bytes, or null when response_format is url. Exactly one of the two fields is populated per entry.
routestring
INFRO extension: which position in the resolved chain rendered the images — primary, or standby_a and so on after a failover. route_name is the slug of the route used, or null for a direct model request.
provider / connectionstring
INFRO extension: the provider kind and the ID of your connection that served the request.
usage.imagesinteger
Number of images the provider returned, which is what the estimate is priced on. Usually equals n.
usage.costnumber | null
INFRO extension: an estimate of what your provider will bill you for this request, with usage.price_source saying which rate it used. null when the model is unpriced.

Image URLs expire 24 hours after the request. Download the bytes or copy them to your own storage if you need them longer — the same 24-hour link applies to async job outputs.

Editing an image

/v1/images/edits takes multipart/form-data rather than JSON, because it carries file uploads. model, prompt, n, size, resolution, aspect_ratio, quality and response_format behave as above and are sent as form fields; seed is accepted but not forwarded on edits. fallbacks and metadata may be sent as JSON-encoded form fields. The whole request body is limited to 51 MB.

imagefilerequired
The image to edit. PNG, JPEG, or WebP, up to 25 MB; a larger file is refused with 400 invalid_request_error. Which dimensions and aspect ratios are accepted is up to the provider.
maskfile
Optional PNG, JPEG, or WebP up to 25 MB, forwarded to the provider with the image. On OpenAI's API it is a PNG with an alpha channel the same size as image, where transparent pixels mark the region to repaint. Without a mask the model edits the whole image guided by the prompt.
promptstringrequired
What the edited region should contain. Describe the desired result, not the change — a walnut reading chair works better than replace the sofa.
curl https://api.infro.io/v1/images/edits \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -F model="bfl/flux-2-dev" \
  -F image=@room.png \
  -F mask=@room-mask.png \
  -F prompt="A walnut reading chair with a brass floor lamp beside it" \
  -F size="1024x1024"

Sizes and aspect ratios

Every model publishes the sizes it can render. These five are common choices; check that your model supports the one you pick:

SizeRatioTypical use
1024x10241:1Avatars, product shots, grid thumbnails
1536x10243:2Blog headers and landscape hero images
1024x15362:3Posters, print, portrait cards
1792x102416:9Slides, video thumbnails, wide banners
1024x17929:16Stories and vertical mobile feeds

An exact size is checked only for its WIDTHxHEIGHT shape and goes to your provider unchanged; a provider that cannot render it picks its own nearest size or refuses the request, according to its own rules. To let INFRO fit the request to each provider instead, send resolution and aspect_ratio: they are checked against what the model takes on each target and sent in that provider's own form (a tier, a ratio, or pixels derived within its limits).

Cost

Providers bill images per image, never per token. The rate depends on the model, the resolution, and quality, and usage.cost on every response estimates what yours will charge. n multiplies it linearly. Vendor list rates live in the catalog; if you have negotiated your own, enter them under Prices and every estimate uses yours instead.

If a connection errors before producing an image, INFRO tries the next target on the route — and then each model in fallbacks — and the cost estimate counts only the images that came back. A request that returns no images is recorded with no cost. Failover needs no change in your code; the provider, connection, and route fields in the response show where it ran.

Errors

  • 400 invalid_request_error — a malformed body or size, n outside 1–10, an upload over 25 MB or not PNG, JPEG, or WebP, or a model of another modality. Refusals from your provider (for example an unsupported size) are reported too.
  • 402 budget_exceeded — the estimated cost of the request has no room under a budget you set. Nothing is rendered and your provider is never called.
  • 413 request_too_large — the request body is over 1 MB on /generations or 51 MB on /edits.
  • 429 rate_limit_exceeded — retry with backoff; see Rate & spend limits.
  • 503 no_available_provider — nothing can serve model. With code no_provider_connected, you have no connection for that vendor — connect one, a retry will not help. Without it, every target on the route is unavailable; retry or widen fallbacks.

Every status code and the full error body are documented in Errors.

↑ ↓ navigate↵ open/ anywhere in docs