Integration · FastAPI

FastAPI image generation without three SDKs in your route handler

Call RenderRoute from a thin service layer. Keep timeouts, idempotency keys, and error mapping boring even when providers are not.

Lilac and gold floating library around a circular portal
FLUX SchnellCloud library960 × 1472
AT A GLANCE

Integrate image generation in FastAPI as a backend-only client with idempotency, explicit timeouts, and typed error handling.

Lilac and gold floating library around a circular portal
Cloud library

“A dreamlike floating library arranged around a circular portal, lilac and gold shelves, soft cloud-blue atmosphere, conceptual editorial art”

FLUX Schnell · fast · 960 × 1472
01

Keep the API route thin and the generation service testable

The HTTP route should parse the Pydantic model, resolve the API principal, read the idempotency and country headers, enforce a scope, and call the generation service. It should not know Runware task fields, Leonardo polling shapes, or Together response formats.

Provider adapters implement one interface. The service owns dimension normalization, safety, rate limits, candidate order, retry classification, circuit breakers, output moderation, cost estimates, idempotency, and usage logging. This division makes provider behavior unit-testable with mocked HTTP responses.

Thin FastAPI routepython
@router.post("/images/generations", response_model=GenerationResponse)
async def generate(
    payload: GenerationRequest,
    request: Request,
    principal: Principal = Depends(get_principal),
    idempotency_key: str | None = Header(None, alias="Idempotency-Key"),
):
    require_scope(principal, "images:generate")
    return await request.app.state.generation_service.generate(
        payload, principal=principal, idempotency_key=idempotency_key,
        country_code=request.headers.get("CF-IPCountry"),
    )
02

Use async I/O for provider calls, but bound concurrency explicitly

Async HTTP prevents a worker from blocking while an inference provider queues or generates. It does not create free capacity. A semaphore protects the process from accepting more simultaneous generations than memory, file descriptors, and provider limits can sustain.

Set provider timeouts and retry counts through server-side deployment configuration. Do not wrap an unbounded retry loop in an async endpoint. For long-running premium work, add a database-backed job queue and return 202 rather than keeping connections open across every deployment layer.

  • One bounded timeout per provider attempt
  • Small retry count with exponential backoff
  • Semaphore or distributed concurrency control
  • Circuit breaker for repeated transient failures
  • Job mode for work beyond the synchronous budget
03

Return a stable error envelope independent of the upstream vendor

Clients need to distinguish invalid_api_key, invalid_dimensions, safety_rejection, rate_limit_exceeded, providers_exhausted, and safety_service_unavailable. Raw provider payloads are inconsistent and may leak account details.

The app converts internal exceptions into one JSON envelope with code, message, request ID, parameter, and sanitized attempt markers. Non-API 404s still render a helpful HTML page, while /v1 errors remain JSON.

LayerRaisesPublic result
PydanticValidation error422 invalid_request
SafetyPolicy or service error400 or 503
Provider adapterClassified provider errorFallback or sanitized API error
RouterExhausted candidates502 providers_exhausted
04

Serve the marketing site with HTML already in the response

The same FastAPI process can serve Jinja2 pages whose primary content, title, canonical, breadcrumbs, FAQ, and JSON-LD are present without client-side rendering. This keeps the SEO layer simple and lets the API and marketing site share model and benchmark data.

Keep interactive tools progressively enhanced. The cost calculator and playground should be useful after JavaScript loads, but their explanatory content and internal links must already exist in server-rendered HTML.

FAQ

Questions about FastAPI image generation API

Why not put the frontend in a separate SPA?

You can, but an SSR Jinja layer is smaller and easier to crawl for this API-first marketing site. A separate app adds deployment and SEO complexity without clear launch value.

Does async FastAPI make generation faster?

It improves server concurrency while waiting on providers. Model inference speed still depends on the provider and request.

Where are provider keys loaded?

From server-side environment variables or a secret manager. Provider keys are never sent to the browser.

Can I use multiple Uvicorn workers?

Yes after replacing the in-process rate limiter and semaphore with distributed controls. Database idempotency already supports shared persistence.

One stable contract

Put the routing layer between your app and the model fleet.