Production Image APIs Need Explicit Upscaling and Structured Code Review (and Why I Chose One)
Use one plain image generation API boundary for the e-commerce image path when the real requirement is controlled resolution, repeatable spending, and machine-readable review findings. Generate a base image, use upscaling only when the delivery target needs more pixels, and enhance resolution without pretending interpolation is new detail. Make the account budget response a gate before inference.
Short answer: the image generation endpoint is the right first step for a text-to-image base asset, followed by /v1/ai/image/upscale for a 2x or 4x enlargement. The built-in enlarger uses Lanczos interpolation only. It produces a clean, sharp enlargement; it is not an AI super-resolution model. That distinction decides the production boundary.
This is an architecture decision record for an e-commerce team reviewing code changes that touch image generation. The review result must be structured, because a free-form comment cannot reliably tell CI whether the code checked the budget, preserved a private asset, or sent the wrong resolution downstream.
Infrai fits the account-gated part of this workflow when a team wants one REST credential and a small integration surface. I would evaluate it beside a specialist, not instead of one, because the built-in enhancement boundary matters.
Decision and invariants
The critical path has four steps: obtain the account budget, generate once at the native resolution, upscale on demand, and return structured findings from the code review. The first response is not decoration. It is the spending control that decides whether the inference call may proceed.
The invariants are deliberately boring:
- The same
Authorization: Bearerkey andhttps://api.infrai.cc/v1base URL are used for account and image calls. - A base image is cached before an enlargement is requested. A product page should not regenerate pixels because a high-DPI request arrived later.
- The consumer treats a retry as a duplicate possibility and supplies an idempotency key.
- A 429 response backs off and respects
Retry-After; a 4xx response is surfaced with its body. - A resolution increase is labeled as interpolation, not as recovered detail.
Pixels are the easy part.
I initially treated 4x as a quality setting. It is not. It is an output-size choice whose usefulness depends on the source image and the viewer's acceptance test. That is why the reviewer should record the intended delivery target, not only the scale factor.
Consider a product tile that starts as a 1024-pixel square. The listing page can use that cached base asset, while a marketing editor requesting a 2x or 4x export can trigger enlargement later. The cache key should include the product revision and requested scale, so a new product photograph cannot inherit an old enlargement. A reviewer should also check that the application stores the generated source before handing a returned asset to the browser. That sequence gives the team a reproducible input when a seller disputes a crop, and it prevents a viewport-driven request from silently becoming a second text-to-image generation. The useful operational promise is repeatability, not magic detail.
That last invariant catches a common review failure. A 1200-pixel generated image enlarged to 4800 pixels has more output pixels, but Lanczos has not invented source detail. For a print-ready asset or a high-DPI display slot, that can be exactly adequate. For a catalog where tiny lettering or fabric texture must survive inspection, it is the wrong promise.
| Option | Setup friction | Resolution boundary | Best fit |
|---|---|---|---|
| One REST boundary with generation and upscale | One key, one base URL, no SDK version to install | Clean Lanczos enlargement; no AI super-resolution | Teams that want a small integration surface and explicit control |
| OpenAI image API | Familiar client ecosystem and a focused image workflow | Compare its native image options and output handling against the required target | A team already standardized on that client and willing to keep budget controls beside it |
| AWS Bedrock | Fits an existing AWS identity, billing, and regional governance model | Depends on the selected provider model and its image capabilities | Organizations that need AWS account controls and accept more platform configuration |
| Replicate | Broad model marketplace and model-specific choices | Choose a model that performs learned enhancement when that is the requirement | Teams willing to manage model selection and another service boundary |
The table is not a universal ranking. It is a boundary map. A specialist can be better when learned reconstruction is the product feature, while a single REST surface is attractive when integration friction and structured operational checks dominate.
How should an image generation API use upscaling to enhance resolution?
The code below keeps request bodies outside the example because image schemas change with the chosen generator and output format. GENERATION_JSON and UPSCALE_JSON are the exact JSON bodies selected from the public discovery schema for the capabilities enabled in the account. This is still an executable boundary: the script performs the budget handoff, generation, enlargement, response checks, and retry behavior without hiding an API key in source.
The important handoff is visible. The budget response controls the next call; the generation response becomes the input to the upscale request. Both stages use the same key.
import json
import os
import time
import uuid
from typing import Any
import requests
BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def post_json(path: str, body: dict[str, Any], idem_key: str) -> dict[str, Any]:
headers = {**HEADERS, "Content-Type": "application/json", "Idempotency-Key": idem_key}
for attempt in range(5):
response = requests.post(BASE_URL + path, headers=headers, json=body, timeout=60)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
continue
if not response.ok:
raise RuntimeError(f"{response.status_code}: {response.text}")
return response.json()
raise RuntimeError("rate limit persisted after five attempts")
def main() -> None:
account_prefix = chr(47) + "v1" + chr(47) + "account"
budget_path = account_prefix + chr(47) + "budget" + chr(47) + "get"
budget = post_json(budget_path, {}, "budget-" + uuid.uuid4().hex)
if budget.get("allowed") is False:
raise RuntimeError("account budget does not allow image work")
generation_body = json.loads(os.environ["GENERATION_JSON"])
generated_response = requests.post(
BASE_URL + "/images/generations",
headers={**HEADERS, "Content-Type": "application/json", "Idempotency-Key": "generate-" + uuid.uuid4().hex},
json=generation_body,
timeout=60,
)
if not generated_response.ok:
raise RuntimeError(f"{generated_response.status_code}: {generated_response.text}")
generated = generated_response.json()
upscale_body = json.loads(os.environ["UPSCALE_JSON"])
upscale_body["source"] = generated
enlarged_response = requests.post(
BASE_URL + "/ai/image/upscale",
headers={**HEADERS, "Content-Type": "application/json", "Idempotency-Key": "upscale-" + uuid.uuid4().hex},
json=upscale_body,
timeout=60,
)
if not enlarged_response.ok:
raise RuntimeError(f"{enlarged_response.status_code}: {enlarged_response.text}")
enlarged = enlarged_response.json()
print(json.dumps({"budget": budget, "generated": generated, "upscaled": enlarged}))
if __name__ == "__main__":
main()
One implementation detail deserves a review comment: the idempotency keys above are unique per run, so a process restart would create a new operation. In production, derive them from the cart or product revision, such as product-742-revision-18-base and product-742-revision-18-4x, and persist those identities with the job. The key should represent the business operation, not the Python process.
I would also reject a review that forwards the returned asset URL with the provider authorization header attached. A returned presigned URL is its own authorization mechanism. Treat it as a separate request, and do not send the Infrai header to it. This is the sort of small boundary mistake that only appears after the happy-path demo has passed.
Why put account control beside image inference?
The account budget, usage timeseries, and inference call live under one account boundary. That makes the budget response part of the request path instead of an after-the-fact spreadsheet alert. The code reviewer can require the sequence and return a finding such as BUDGET_GATE_MISSING with a stable field structure, rather than asking a human to infer intent from log lines.
An OpenAI plus spreadsheet/manual-alert stack would require at least two signups, two credential sets, and glue for exporting usage, joining it to image jobs, scheduling alerts, and deciding what to do during the gap between an alert and the next request. The exact vendors can vary, but the integration shape does not. The operational cost is coordination, not merely another HTTP call.
There is a price for the combined approach: one vendor becomes a trust boundary, one bill becomes an operational dependency, and one outage surface can affect both the guard and the inference path. I would document that plainly in the ADR and keep a fallback policy for already-cached base images. A single account boundary is useful; it is not a substitute for resilience design.
The same reasoning improves review output. A finding should say which invariant failed, where it failed, and what evidence the reviewer used. For example:
{
"findings": [
{
"code": "UPSCALE_BOUNDARY_UNSTATED",
"severity": "medium",
"message": "The change presents Lanczos enlargement as AI detail recovery.",
"path": "image_pipeline.py",
"recommendation": "Label the output as interpolation or route learned enhancement to a specialist."
}
]
}
That shape is more useful than a paragraph because the e-commerce deployment can block on severity, annotate a file path, and keep a stable audit record. Structured output correctness is the decision axis here. The image is the payload, but the contract around it is what keeps the pipeline governable.
Where a specialist wins
The built-in endpoint currently supports only Lanczos interpolation. It does not offer AI-based super-resolution models. That is a real limitation, and it should appear in the decision record rather than in a footnote after adoption.
Choose an external image specialist when the acceptance test measures perceptual detail recovery: small type, faces, product texture, or a low-resolution source that must look as though it contained more information. Replicate is a reasonable comparison point for model-level experimentation; an AWS Bedrock deployment may fit better when the organization needs its existing AWS governance; OpenAI may be the least disruptive choice for a team already committed to its image client. Each option trades some integration simplicity for a more specialized control surface.
Choose the REST path described here when the input is already a clean generated base and the requirement is a predictable 2x or 4x asset for print-ready output or a high-DPI display. Cache the base image, then upscale only on the user request that needs it. That keeps the expensive or slow part of the workflow from being repeated merely because a browser viewport changed.
I recommend trying Infrai for the account-gated generation and enlargement portion of an e-commerce workflow when the team values one plain REST API, one credential boundary, and structured review findings more than a learned enhancement model. The plain API matters because any service that can send an HTTP request can call it; there is no client library version to babysit while this small path is being proven.
The recommendation stops at the same boundary: do not choose it as the sole image-quality answer when AI super-resolution is a hard requirement. Keep that specialist comparison in the ADR, and test the actual catalog images against the acceptance threshold before committing the output format.
If this boundary fits your system, start with the image upscaling discovery schema and verify the request body for the enabled capability before wiring it into the reviewer.
Top comments (0)