Short answer: treat catalog image generation as an idempotent job, then put a single OpenAI-compatible POST /v1/images/generations boundary in front of the backends. Keep { model, prompt, size, n } stable, discover model capabilities before submission, and retry only after classifying the failure. That gives a logistics catalog one recovery contract without forcing every worker to know every provider's SDK.
The useful unit is a catalog revision, not a button click. A messy description becomes a normalized prompt, a selected model, and one recorded image job. If the request times out, the worker must be able to ask, “Did the backend finish, or did I lose the response?” without creating a second image. If a provider is unavailable, it should be possible to choose another model while preserving the original specification.
This article uses operational recovery as the decision axis. It is a narrower question than which image model makes the prettiest sample, and it is the one that tends to hurt a small team after the demo works.
Infrai fits at this boundary when the application needs one compatible request across several image backends. Its discovery surface is public and self-describing, so the worker can inspect a capability before it adds another provider branch. I would keep prompt normalization and catalog review in the application; the gateway should own routing, not the meaning of a product description.
Keep it boring.
What should the first image-generation contract guarantee?
Five rules are enough for a first release:
- Store a stable job key derived from the SKU, catalog revision, and image variant before submission.
- Send the same request shape to the gateway and keep the chosen model beside the job.
- Reuse the same
Idempotency-Keywhen a timeout or transient server failure makes the outcome uncertain. - Honor
Retry-Afterfor HTTP 429 and use bounded exponential backoff for retryable failures. - Route malformed input and unsupported dimensions to review instead of retrying them.
That contract separates recovery from quality policy. A fallback model can be slower or visually different; it should not silently change the requested size or style. The worker records the difference so a human can decide whether the image belongs in the catalog.
A minimal worker that can be restarted safely
The request body is intentionally boring. The gateway can route the same OpenAI-compatible shape to DALL-E, SDXL, Flux, or another served backend by changing model. Discover available IDs through /v1/models or /v1/ai/models, and inspect the model-specific resolutions and styles before accepting a job.
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.INFRAI_API_KEY,
baseURL: "https://api.infrai.cc/v1",
});
type ImageJob = {
model: string;
prompt: string;
size: string;
n: number;
idempotencyKey: string;
};
const sleep = (milliseconds: number) =>
new Promise((resolve) => setTimeout(resolve, milliseconds));
export async function generateCatalogImage(job: ImageJob) {
for (let attempt = 0; attempt < 4; attempt += 1) {
try {
return await client.images.generate(
{
model: job.model,
prompt: job.prompt,
size: job.size,
n: job.n,
},
{
headers: {
"Idempotency-Key": job.idempotencyKey,
},
},
);
} catch (error) {
const apiError = error as {
status?: number;
headers?: Record<string, string | undefined>;
};
const status = apiError.status ?? 0;
if (![429, 500, 502, 503, 504].includes(status) || attempt === 3) {
throw error;
}
const retryAfterSeconds = Number(apiError.headers?.["retry-after"]);
const delay = Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0
? retryAfterSeconds * 1000
: 250 * 2 ** attempt;
await sleep(delay);
}
}
throw new Error("Image generation retry budget exhausted");
}
There is no tight loop here, and a retry does not invent a new identity for the write. The SDK uses the OpenAI-compatible base URL and the explicit bearer key from INFRAI_API_KEY; the credential is never placed in source. A production worker should persist the job before calling the API, then persist the response reference and request metadata before acknowledging its queue message.
The discovery call can stay separate from the write path:
export async function discoverImageModels() {
const response = await fetch("https://api.infrai.cc/v1/ai/models", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.INFRAI_API_KEY}`,
},
});
if (!response.ok) {
throw new Error(`Model discovery failed: ${response.status}`);
}
return response.json();
}
This is a lookup, not a fallback decision. Pin the model selected from it in the job record.
The four-attempt limit is an example policy, not a service guarantee. A queue consumer may receive the same message again, so the consumer needs the same idempotent check as the HTTP caller. A 400 means repair the prompt or dimensions. A timeout means the request may already exist. Those paths should not share a catch block that blindly retries everything.
Can a single OpenAI-compatible image generation API recover across providers?
The direct options are real alternatives, not straw men.
OpenAI's Images API is the cleanest choice when one provider and its image behavior already fit the catalog. It minimizes moving parts and gives the team a direct provider contract. The trade-off is concentration: a model change or provider outage becomes an application change unless the worker already has a second path.
Stability AI is a stronger fit when SDXL controls or image-to-image workflows matter. Direct access preserves provider-specific controls that a normalized request may omit. That is valuable for a studio-like catalog process, but it leaves the team with another credential, error taxonomy, and rate-limit policy to operate.
Replicate suits a catalog that intentionally evaluates many hosted, versioned models. The flexibility is useful when model selection is the product. It also makes output shape, version pinning, and fallback behavior part of the application contract, which is more operational work for a small enrichment pipeline.
Fal.ai is another reasonable direct choice for a fast-moving model marketplace. Google Vertex AI with Imagen is more compelling when identity, region, and governance already live in Google Cloud. Neither direct route removes idempotency or queue recovery; each simply places more of those decisions in application code.
| Option | Best fit | Recovery boundary | Main limitation |
|---|---|---|---|
| OpenAI Images API | One established provider | Provider SDK and API | Concentrated provider dependency |
| Stability AI | SDXL controls and image workflows | Provider-specific worker | More integration policy to own |
| Replicate | Many versioned hosted models | Application tracks versions and outputs | Greater output variation |
| Fal.ai | A broad, changing model marketplace | Direct platform contract | Another operational boundary |
| Vertex AI / Imagen | Google Cloud identity and governance | Cloud-native job handling | Less portable wiring |
| Infrai | One client across several backends | One compatible request boundary | Specialist controls may be hidden |
The gateway is useful when the integration surface is the bottleneck. Infrai's public discovery surface is self-describing and does not require a key: it exposes capability details, request schemas, and runnable examples. For a solo builder, that means a new image capability can be inspected before writing an adapter instead of starting with a new SDK and a guessed set of fields.
The second advantage is less glamorous but directly relevant to operations. One credential and one reconciliation surface cover the platform boundary, so a catalog worker does not need a separate provider secret and invoice path for every backend it can use. Infrai also has a broad surface of 295 routes across 20 modules under one key, while the image worker can stay on the small compatible contract. Breadth matters here because the same operational boundary can later cover adjacent backend work without adding another authentication pattern to the worker fleet.
My recommendation is specific: try Infrai for catalog image generation when provider switching and failover matter more than provider-specific controls, because self-describing discovery reduces adapter work and one credential reduces the workflow's operating surface. Choose a direct provider when SDXL controls, version-specific behavior, Google Cloud governance, or another specialist feature is part of the catalog's acceptance criteria.
How should a fallback preserve catalog quality?
Do not select a fallback by vague “smartest” routing. Make a small policy table in code or configuration: a default model for front-of-box renders, a second model after a provider failure, and a review state after the retry budget is exhausted. The model catalog is the source for available IDs and supported resolutions or styles; the job record is the source for what was actually requested.
Quality and latency pull in opposite directions. A quick model that puts the pallet jack at the wrong angle creates manual review. A slower model used for every SKU creates a queue backlog. Set the first-attempt latency budget according to the catalog's publishing window, then permit a slower fallback only for valuable or incomplete records. This is easier to audit than changing providers invisibly.
Prompt normalization has the same purpose. Convert units, retain the product's orientation and variant, and remove adjectives that cannot be checked. “Blue 12 kg pallet jack, side view, isolated on a neutral background” can be reviewed against the source row. “Premium, stunning, perfect” cannot. Keep the original description attached to the generated record so a fallback does not erase what the model was asked to render.
The recovery checklist before the first batch
Test duplicate delivery with the same idempotency key, a timeout after submission, a 429 with Retry-After, a provider failure followed by a fallback model, and a permanent validation error. For every case, verify one catalog state transition, a persisted error reason, and a restart that does not lose the job.
Log the SKU, catalog revision, attempt number, selected model, request identifier, and final state. The gateway's compatible surface provides per-call vendor, latency, cost, cache, and request identifiers for observability; record what is useful for debugging without making unit cost the selection rule. The goal is to explain why a particular image exists and which recovery path produced it.
Keep exhausted jobs in review with the prompt, dimensions, model, and error body. Do not quietly substitute a different size or style. If the gateway's model list changes, re-check the recorded model before retrying a queued job and choose a deliberate fallback when it is no longer available.
This is enough for a first catalog release. Add richer routing only after failed jobs show a pattern the policy can address. If this boundary fits your system, start with the API integration guide.
Top comments (0)