Reaching a specific video vendor without its own account
Pin the vendor on the generate call and skip the provider onboarding. What pinning gives up, and how to tell which vendors and models are reachable today.
Getting a generation from a specific video vendor normally means an account with that vendor, a project, a billing profile and a set of credentials to rotate. On Infrai it’s a field: POST /v1/video/generate accepts a vendor value, and the route’s own discovery record publishes which values are accepted right now. No provider onboarding, no second invoice, one key.
What you give up by pinning is worth understanding before you do it, because the default behaviour is usually better.
Which vendors are accepted today
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",
"vendors": ["tencent_vod", "wanxiang", "alibaba_intl", "veo"],
"vendors_ready": ["tencent_vod", "alibaba_intl", "veo"],
"vendors_pending": ["wanxiang"],
"default_vendor": "tencent_vod",
"dynamic_params": { "vendor": ["tencent_vod", "alibaba_intl", "veo"] },
"billing": {
"is_billable": true, "unit": "per_second", "price_usd": 0.09,
"approximate": true, "new_account_trial_uses": 22
}
}
dynamic_params.vendor is the authoritative list of values the route will accept — read it at runtime rather than hardcoding, because it changes as vendors are onboarded. vendors_ready versus vendors_pending is the readiness distinction: pending means the vendor is known to the platform but not yet serving.
default_vendor is what you get when you don’t pin anything.
Pin a vendor
curl -sS -X POST "https://api.infrai.cc/v1/video/generate" \
-H "Authorization: Bearer ${INFRAI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a hawk banking over a canyon at golden hour, tracking shot",
"vendor": "veo",
"resolution": "720p",
"duration_seconds": 5,
"aspect_ratio": "16:9",
"store": true
}'
The response echoes the vendor and model that actually served the job, which is the field to log — a pin you thought applied and didn’t is otherwise invisible.
Vendor pin versus model pin
These are two different levers and the distinction matters.
GET /v1/video/capabilities enumerates named models with their vendor, region, max_seconds and whether they support image-to-video. Naming a model from that list is the precise choice: you get that model, and you know its clip-length ceiling in advance.
Pinning only a vendor is the looser choice: you get that vendor, and it selects the model. For vendors whose models the catalogue enumerates, naming the model is strictly more informative. For a vendor the catalogue doesn’t enumerate, the vendor pin is how you reach it at all — which is the case for veo at the time of writing, where dynamic_params accepts the vendor and the model catalogue lists the China-region models from the other two.
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"})
def accepted_vendors() -> list[str]:
"""Read the accepted values rather than hardcoding them. A vendor list baked
into your code is a list that is wrong the week a vendor is added."""
resp = SESSION.get(f"{API}/v1/discovery/video.generate", timeout=25)
resp.raise_for_status()
body = resp.json()
return list((body.get("dynamic_params") or {}).get("vendor") or body.get("vendors_ready") or [])
def catalogue() -> list[dict]:
resp = SESSION.get(f"{API}/v1/video/capabilities", timeout=25)
resp.raise_for_status()
return resp.json()["data"].get("models", [])
def generate(prompt: str, *, vendor: str | None = None, model: str | None = None,
seconds: int = 5) -> dict:
"""Prefer a named model; fall back to a vendor pin; default to neither, which
lets the platform choose and fail over."""
if model:
known = {m["model"] for m in catalogue()}
if model not in known:
raise ValueError(f"{model} is not in the live catalogue")
if vendor and vendor not in accepted_vendors():
raise ValueError(f"{vendor} is not an accepted vendor value today")
body = {"prompt": prompt, "resolution": "720p", "duration_seconds": seconds,
"aspect_ratio": "16:9", "store": True}
if model:
body["model"] = model
if vendor:
body["vendor"] = vendor
resp = SESSION.post(f"{API}/v1/video/generate", json=body, timeout=60)
payload = resp.json()
if not payload.get("ok"):
raise RuntimeError(payload["error"]["code"])
data = payload["data"]
# Log what actually served it. A pin you believed applied and didn't is the
# kind of assumption that survives until a cost review.
return {"job_id": data["job_id"], "vendor": data.get("vendor"), "model": data.get("model")}
if __name__ == "__main__":
print({"vendors": accepted_vendors(), "models": [m["model"] for m in catalogue()]})
print(generate("a hawk banking over a canyon at golden hour", vendor="veo"))
What pinning costs you
| Choice | Failover | Precision | When it’s right |
|---|---|---|---|
| Neither vendor nor model | yes — platform picks and can retry elsewhere | none | most workloads |
model from the catalogue | no — that model or nothing | exact | you need this model’s look |
vendor only | within that vendor | partial | reaching a vendor with no catalogue entry |
The first row is the honest recommendation. A pinned job can’t fail over — if that vendor is having a bad hour, your job is having a bad hour, and you’ve traded the main advantage of going through a gateway for a preference you may not need.
Pin when the output’s character genuinely differs and your users would notice. Don’t pin because a model was recommended in a blog post.
Limitations
A vendor pin doesn’t grant you that vendor’s full parameter surface. You get the common request shape — prompt, model, resolution, duration, aspect ratio, seed, negative prompt, reference image — not every knob the provider’s own API exposes. If a specific vendor’s advanced controls are the reason you chose it, going direct is the better fit and this isn’t a good substitute.
The catalogue also doesn’t enumerate models for every accepted vendor, so “which model will serve my pinned vendor” is answered by the response rather than in advance. And a pinned vendor that stops being ready means your requests need a code change, where an unpinned job would simply have been served elsewhere.
What you keep either way is the surrounding pipeline on one credential: the queue that submits the job with POST /v1/queue/publish, the archive with PUT /v1/storage/object/put/{bucket}/{key} before retention_days expires, and one GET /v1/account/usage pricing all of it. Generation bills per second of output, live in GET /v1/discovery/video.generate and approximate: true because it varies by vendor (verified 2026-09-21) — read it there, and expect platform rates to drift downward as vendor contracts improve.