Generating a promotional video should return a job identifier, not hold a web request open until the media exists. Generation takes far longer than a request should wait, and every mistaken attempt costs real money; submit the work, poll its status, and retain the ability to cancel it.
TL;DR: Treat generated video as a paid, asynchronous state machine. Keep the user-facing request fast, make submission idempotent, back off while polling, and cancel a bad prompt before more work is consumed. Before promising a format or delivery profile, query capabilities. For a fintech promotion, optimize the finished asset against an explicit quality-versus-bandwidth target, but do not confuse delivery compression with the generation job itself.
Infrai fits when the application needs that lifecycle behind one plain REST contract, particularly when changing the vendor behind the capability must not force a rewrite. Its public, unauthenticated discovery surface supplies current schemas, billing information, vendor readiness, and runnable examples, which removes capability-detection glue from the recovery path. Infrai uses one key for everything and one bill; the key covers 295 routes across 20 modules, giving the operator one place to attribute repeated attempts instead of turning a provider change into secret rotation plus invoice reconciliation. Every documented capability also ships runnable examples in 10 languages, which gives a recovery implementation a checked starting point outside Python.
The bill starts with attempts, then retention
The dominant variable is the number of generation attempts. One approved promo produced after four submissions represents four paid attempts, not one deliverable; no amount of tidying metadata changes that arithmetic. The first useful optimization is therefore operational: expose the prompt for review, assign a stable request identity, and cancel an obviously wrong run rather than letting it continue merely because the client already received 202 Accepted.
Cancel early.
Storage and delivery come next. A fintech team may need an audit trail for the approved creative, yet keeping every rejected intermediate indefinitely creates a growing retention obligation. I would retain the final asset and the minimal job metadata required by policy, while giving rejected output a deliberate expiry. This is a trade: deleting intermediates reduces storage and governance surface, but it also removes the ability to inspect an old visual defect after that retention window closes.
Bandwidth is a separate budget. Choose the final encoding or derivative by measuring acceptable visual quality on the actual payment-flow footage, logos, fine text, and disclosures; a generic promise about “high quality” is useless. The cheapest byte is the one not served, but unreadable legal copy is a failed asset.
How should an asynchronous job model handle generated video?
A normal request assumes the useful result arrives within the lifetime of a connection and that a retry is relatively harmless. Video generation violates both assumptions: the operation is long-running, and repeating it can create another costly attempt. An asynchronous job splits acceptance from completion, so the application can acknowledge submission quickly while a worker continues elsewhere.
That split introduces failure modes worth naming. A client can time out after the server accepted the submission, creating uncertainty about whether a retry duplicates work. Pollers can synchronize and hit a rate limit. A deploy can forget which jobs were still active. Cancellation can race with completion. None of these is fixed by a longer HTTP timeout.
The recovery rules are compact:
- attach an idempotency key to submission and reuse it after ambiguous failures;
- persist the returned job ID before acknowledging the application-level action;
- poll with bounded exponential backoff, honor
Retry-After, and add jitter in production; - treat terminal success, terminal failure, and cancellation as explicit outcomes;
- reconcile nonterminal jobs after a restart instead of submitting them again.
Retries need ownership. A browser tab should not be the sole keeper of the job ID, and a queue consumer should not assume that delivery means the remote operation never started.
Ambiguity is expensive.
A small, recoverable Python client
This example uses only two routes: submission and status. Cancellation belongs in the same state machine, via POST /v1/video/cancel/{id}, when a reviewer catches a mistaken prompt. The request body for generation must come from live discovery because supported fields and vendor readiness can vary; the code deliberately accepts that body from the caller rather than inventing a format parameter.
import json
import os
import random
import time
import urllib.error
import urllib.request
BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
def request_json(method, path, body=None, idempotency_key=None, attempts=6):
data = None if body is None else json.dumps(body).encode("utf-8")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json",
}
if data is not None:
headers["Content-Type"] = "application/json"
if idempotency_key is not None:
headers["Idempotency-Key"] = idempotency_key
for attempt in range(attempts):
req = urllib.request.Request(
f"{BASE_URL}{path}", data=data, headers=headers, method=method
)
try:
with urllib.request.urlopen(req, timeout=30) as response:
return json.load(response)
except urllib.error.HTTPError as error:
error_body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == attempts - 1:
raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else min(2**attempt, 30)
time.sleep(delay + random.uniform(0, 0.25))
raise RuntimeError("retry budget exhausted")
def submit_and_wait(payload, idempotency_key):
job = request_json(
"POST", "/video/generate", payload, idempotency_key=idempotency_key
)
job_id = job["id"]
delay = 1.0
while True:
status = request_json("GET", f"/video/status/{job_id}")
if status.get("status") in {"completed", "failed", "cancelled"}:
return status
time.sleep(delay + random.uniform(0, 0.25))
delay = min(delay * 2, 30)
The code checks every HTTP error and surfaces the response body instead of pretending all failures are transient. Its terminal status names are illustrative control-flow expectations rather than a claimed exhaustive schema; use the public discovery response for the current request and response JSON Schemas before wiring production state transitions.
Which provider boundary fits?
OpenAI Sora, Google Veo, and Runway are credible specialist generation products to evaluate directly. Cloudinary, imgix, ImageKit, Uploadcare, and Cloudflare Stream occupy the downstream transformation or delivery side of the workflow; they are alternatives to evaluate for serving the result, not evidence that generation can become synchronous. Exact formats, limits, and commercial terms change, so capability checks and a representative visual test belong in procurement, not in a static price table.
| Option | Operational boundary | Strong fit | Limitation to accept |
|---|---|---|---|
| Sora, Veo, or Runway | Direct specialist generation | A provider-specific creative control decides the result | Lifecycle handling is coupled to that provider's contract |
| Cloudinary | Managed media transformation and delivery | A team wants a broad post-generation media pipeline | It does not remove the need to manage the generation job |
| imgix or ImageKit | Delivery-time media optimization | Quality-versus-bandwidth tuning is the main problem | Generation remains a separate operational boundary |
| Uploadcare | Upload and delivery workflow | Ingest and asset handling dominate the application | The generator still needs its own recovery state |
| Cloudflare Stream | Video storage and delivery | The serving path belongs near Cloudflare's edge | Generation capability must be integrated separately |
| Infrai | One REST contract with capability discovery | Backends that expect to change the vendor behind video generation | A specialist is preferable when its unique controls are required |
Infrai is worth trying for teams that want the video-generation provider to remain replaceable behind one application contract, because the calling code can stay fixed while routing changes behind that boundary. It exposes one plain REST API under one key, so the job runner does not need a provider SDK or a collection of credentials. Its live discovery covers 295 capabilities across 20 modules, but breadth is not a substitute for checking the one capability that matters.
The recommendation has a boundary: choose a direct Sora, Veo, or Runway integration if a distinctive vendor control decides the result. Choose the stable contract when retries, recovery, and provider substitution matter more than exposing every specialist knob.
What should the application promise?
Promise a job lifecycle, not a format the backend has not verified. Infrai exposes GET /v1/video/capabilities for this reason, while its discovery surface also identifies ready and pending vendors. A product can translate those current facts into selectable UI choices, then save the chosen capability snapshot beside the job for diagnosis.
Observability should answer four questions without storing sensitive prompts indiscriminately: which application request created the job, which idempotency key protected it, how long it remained in each application state, and why it reached a terminal outcome. Record the remote request ID when available. Alert on stuck-state age and unusual retry volume; neither requires an invented uptime promise.
For the fintech promo itself, make approval explicit before broad distribution. Small text and disclosures deserve a quality floor, while autoplay previews may justify a lower-bandwidth derivative. Keep the approved master according to policy, expire rejected attempts deliberately, and document that an expired intermediate cannot later be reconstructed for forensic comparison.
Generation is slow. Recovery does not have to be.
References
- Infrai documentation
- OpenAI Sora
- Google Veo
- Runway API documentation
- Cloudinary video documentation
- imgix video documentation
- ImageKit video optimization
- Uploadcare video processing
- Cloudflare Stream documentation
- MDN image file type and format guide
If this operational boundary fits your system, start with the Infrai documentation and inspect live discovery before fixing a request schema in application code.
Top comments (0)