AI video cost per second, and capping a single job
Duration times a resolution multiplier is the whole formula. Where the 4x jump hides, why a budget cap won't stop one big job, and the pre-flight check that will.
Video is the most expensive thing on most Infrai accounts per unit of output, and unlike a per-call charge it scales with how much you asked for. POST /v1/video/generate bills per second of output, and GET /v1/video/capabilities publishes a resolution_multiplier that decides how much a second costs: 720p is 1, 1080p is 2, 4k is 4.
So the cost of a job is duration times that multiplier. Two numbers, both under your control, and both easy to get wrong by a factor of four.
The rate and the multiplier
curl -sS "https://api.infrai.cc/v1/discovery/video.generate" \
-H "Authorization: Bearer ${INFRAI_API_KEY}"
{
"id": "video.generate",
"method": "POST",
"path": "/v1/video/generate",
"minimum_tier": "standard",
"vendors_ready": ["tencent_vod", "alibaba_intl", "veo"],
"billing": {
"is_billable": true,
"billing_class": "floored",
"unit": "per_second",
"price_usd": 0.09,
"currency": "USD",
"approximate": true,
"new_account_trial_uses": 22,
"note": "new accounts get $2 free → up to 22 free seconds (varies by vendor/model — cheapest shown)"
}
}
Read that live rather than trusting this page: approximate: true means it varies by vendor and model, and the figure shown is the cheapest. Verified 2026-09-21, and platform rates drift downward as vendor contracts improve.
curl -sS "https://api.infrai.cc/v1/video/capabilities" \
-H "Authorization: Bearer ${INFRAI_API_KEY}"
{
"ok": true,
"data": {
"models": [
{"vendor": "tencent_vod", "model": "Kling:2.5", "region": "china", "max_seconds": 12, "image_to_video": true},
{"vendor": "alibaba_intl", "model": "wan2.7-t2v", "region": "china", "max_seconds": 15, "image_to_video": true}
],
"resolutions": ["720p", "1080p", "4k"],
"resolution_multiplier": {"720p": 1, "1080p": 2, "4k": 4}
}
}
Where the money actually goes
The 4x multiplier is the trap, because resolution is the field people set once and forget. A fifteen-second 4k clip is sixty billable units against a five-second 720p clip’s five — twelve times the cost, for output that in most social contexts gets downscaled before anyone sees it.
Generate at 720p while iterating on prompts. Render the accepted version at the resolution you actually publish.
| Choice | Billable units (duration × multiplier) |
|---|---|
| 5s at 720p | 5 |
| 5s at 1080p | 10 |
| 15s at 1080p | 30 |
| 15s at 4k | 60 |
That table is the whole cost model. Nothing else you can change moves the number as much.
A budget cap won’t save you here
Worth being precise: PUT /v1/account/budget/set bounds your total spend over a period. It does not stop one expensive job, because a single fifteen-second 4k render can sit comfortably under any sensible cap and still cost more than you meant to spend on one clip.
The control you need is a pre-flight check in your own code:
import os
import requests
API = "https://api.infrai.cc"
KEY = os.environ["INFRAI_API_KEY"]
SESSION = requests.Session()
SESSION.headers.update({"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
MAX_UNITS_PER_JOB = 10 # duration x multiplier — your own policy
DRAFT_RESOLUTION = "720p"
def capabilities() -> dict:
resp = SESSION.get(f"{API}/v1/video/capabilities", timeout=25)
resp.raise_for_status()
return resp.json()["data"]
def rate_usd_per_second() -> float:
resp = SESSION.get(f"{API}/v1/discovery/video.generate", timeout=25)
resp.raise_for_status()
return float((resp.json().get("billing") or {}).get("price_usd") or 0.0)
def estimate(duration_seconds: int, resolution: str) -> dict:
"""Estimate before you spend. The rate is approximate and varies by vendor, so
treat this as a floor rather than a quote — and read cost_usd off the finished
job for the real figure."""
caps = capabilities()
multiplier = (caps.get("resolution_multiplier") or {}).get(resolution)
if multiplier is None:
raise ValueError(f"unknown resolution {resolution}; available: {caps.get('resolutions')}")
units = duration_seconds * multiplier
return {"units": units, "estimate_usd_floor": round(units * rate_usd_per_second(), 4),
"multiplier": multiplier}
def generate_guarded(prompt: str, duration_seconds: int, resolution: str, model: str) -> dict:
quote = estimate(duration_seconds, resolution)
if quote["units"] > MAX_UNITS_PER_JOB:
raise RuntimeError(
f"refusing: {quote['units']} units exceeds the per-job ceiling of {MAX_UNITS_PER_JOB}. "
f"Try {DRAFT_RESOLUTION} or a shorter duration."
)
resp = SESSION.post(
f"{API}/v1/video/generate",
json={"prompt": prompt, "model": model, "resolution": resolution,
"duration_seconds": duration_seconds, "aspect_ratio": "9:16", "store": True},
timeout=60,
)
body = resp.json()
if not body.get("ok"):
raise RuntimeError(body["error"]["code"])
return {"job": body["data"], "quote": quote}
if __name__ == "__main__":
print(estimate(15, "4k"))
print(generate_guarded("a paper boat drifting down a gutter after rain", 5, "720p", "Kling:2.5"))
A per-job ceiling expressed in units rather than dollars survives a repricing, which is why it’s the right shape for the check.
Read the real cost afterwards
curl -sS "https://api.infrai.cc/v1/video/get/vid_2fVc8nRqLmT4xBzY" \
-H "Authorization: Bearer ${INFRAI_API_KEY}"
The finished job carries cost_usd — the actual charge, accounting for vendor, model and resolution. Log it against your own job record and you’ll have a per-clip cost history nobody had to estimate.
For the account view, GET /v1/account/usage breaks spend down by capability and GET /v1/account/balance returns runway_days plus an affordable_uses_hint telling you how many calls of each kind your remaining credit buys. Both free reads.
The structural facts worth planning on
Three things are policy rather than rate. Generation is billed on output seconds, so a failed generation that produced nothing shouldn’t carry the full charge — check cost_usd on a failed job rather than assuming. Cancelling a running job with POST /v1/video/cancel/{id} stops further work, so a job you can tell is going wrong is worth cancelling rather than waiting out. And a new account starts with $2, which the platform’s own note puts at roughly 22 seconds at the cheapest rate — enough to measure your acceptance rate before committing.
Limitations
approximate: true on the billing block means you cannot get an exact quote before running a job: vendor and model both move the rate, and the figure discovery shows is the cheapest available. If your product needs to show a customer a firm price before generating, you’ll have to add your own margin and reconcile against cost_usd afterwards — that’s a real limitation and it’s the reason per-clip billing to end users is awkward here.
Going direct to a single model provider gets you one published rate card and predictable arithmetic, which is genuinely simpler if you only ever use one model. What you trade is the ability to move between a dozen models on a string change, and the fact that the storage the clip lands in, the queue that submitted it and the bill that prices it are all one credential and one GET /v1/account/usage.