AI Video Generation API for SaaS Products | Integration Guide 2026
You're building a SaaS product — a social media scheduler, a marketing automation tool, an e-commerce platform — and you want to add AI video generation as a feature. The question your architecture team is actually asking isn't "which model produces the most impressive clips"; it's "which API setup lets us ship reliably, track costs per user, and scale without accumulating ops debt from N separate integrations."
This guide answers those questions: which models fit which SaaS verticals, how to handle async job state in a multi-tenant backend, how to attribute generation costs to individual users, and what your monthly COGS looks like at different usage scales.
What Makes a Video Generation API SaaS-Ready?
A SaaS-ready video API needs three properties that aren't always obvious from marketing pages:
- Consistent async job model — generation takes 30–120 seconds; you need a stable job ID, a queryable status endpoint, and deterministic completion states (
succeeded/failed), not timeouts or streaming-only responses. - Per-call pricing with no minimums — SaaS usage is spiky. APIs that require prepaid compute credits or have high monthly floors bloat your COGS before you have volume to justify it.
- Single integration surface — if your product needs to route between models (quality tiers, user plan tiers), maintaining separate credential sets and request schemas for each model provider multiplies your integration surface and your on-call surface.
GenRelay is a unified generative media API: one API key, one endpoint schema, one async pattern across Veo 3.1, Grok Imagine 1.0/1.5, and Gemini Omni Flash. You swap models by changing a single field — "model": "veo-3.1" vs "model": "grok-imagine-1.0" — while the rest of your integration stays identical. See integrating AI video generation for a full walkthrough.
Which Video Model Fits Which SaaS Use Case?
The answer depends on what your users are generating and what they're paying for:
| SaaS Vertical | Use Case | Recommended Model | Rationale |
|---|---|---|---|
| Social media tools | Short clips from text prompts | Grok Imagine 1.0 | Lowest per-second cost for t2v; 720p sufficient for social |
| Marketing platforms | High-fidelity brand videos | Veo 3.1 Lite | 1080p/4K support; best motion quality in the catalog |
| E-commerce / product | Animating product photos | Veo 3.1 (i2v) or Grok 1.5 (i2v) | Both support image-as-input; Grok 1.5 is 3× cheaper |
| Content creation tools | Rapid iteration, high volume | Gemini Omni Flash | Flat per-generation fee; fastest turnaround |
| Video editing SaaS | Reference-guided video generation | Veo 3.1 (ref2v) | Supports motion-style reference video alongside the prompt |
August 2026 pricing reference:
| Model | Billing | 720p | 1080p | 4K |
|---|---|---|---|---|
| Veo 3.1 Lite (t2v / i2v / ref2v) | per second | $0.060/s | $0.120/s | $0.180/s |
| Grok Imagine 1.0 (t2v) | per second | $0.010/s | — | — |
| Grok Imagine 1.5 (i2v) | per second | $0.022/s | — | — |
| Gemini Omni Flash | per generation | $0.10 | $0.15 | — |
For a social SaaS where users generate 5-second clips, Grok 1.0 at $0.050/clip gives the lowest variable cost. For a marketing platform where 1080p quality is a selling point, Veo 3.1 at $0.60/clip (5s, 1080p) is the only option in the catalog.
How Do You Structure Async Video Jobs in a Multi-Tenant Backend?
Video generation cannot block an HTTP request — a 5-second clip takes 30–90 seconds to generate. The correct architecture for SaaS:
- User triggers generation → your backend submits the job to GenRelay → stores
job_idin your database → returns202 Acceptedto the client immediately - A background worker polls GenRelay → on completion, stores the video URL and notifies the user
Here's the submission side:
import os, requests
KEY = os.environ["GENRELAY_API_KEY"]
BASE = "https://genrelay.ai/v1"
HEADERS = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
def submit_video_job(user_id: str, prompt: str, model: str, duration: int) -> str:
"""Submit a video generation job; return job_id for tracking."""
r = requests.post(
f"{BASE}/videos/generations",
headers=HEADERS,
json={"model": model, "prompt": prompt, "duration": duration, "resolution": "720p"},
timeout=30,
)
r.raise_for_status()
job_id = r.json()["id"]
# Persist to your database
db.jobs.insert({
"user_id": user_id,
"job_id": job_id,
"model": model,
"duration": duration,
"status": "queued",
})
return job_id
And the polling worker (run as a Celery task, a cron job, or a background asyncio loop):
import time
COST_PER_SECOND = {
"veo-3.1": {"720p": 0.060, "1080p": 0.120, "4k": 0.180},
"grok-imagine-1.0": {"720p": 0.010},
"grok-imagine-1.5": {"720p": 0.022},
}
FLAT_FEE = {
"omni-flash": {"720p": 0.10, "1080p": 0.15},
}
def poll_and_settle(job_id: str):
"""Poll GenRelay until done; credit user and store video URL on success."""
job = db.jobs.get(job_id=job_id)
for _ in range(40): # up to 400s total
time.sleep(10)
data = requests.get(
f"{BASE}/videos/generations/{job_id}", headers=HEADERS, timeout=15
).json()
if data["status"] == "succeeded":
model, resolution = job["model"], job.get("resolution", "720p")
if model in COST_PER_SECOND:
cost = COST_PER_SECOND[model][resolution] * job["duration"]
else:
cost = FLAT_FEE.get(model, {}).get(resolution, 0)
db.jobs.update(job_id, status="done",
video_url=data["output"]["url"], cost_usd=cost)
db.users.deduct_credits(job["user_id"], cost)
notify_user(job["user_id"], data["output"]["url"])
return
if data["status"] == "failed":
db.jobs.update(job_id, status="failed", error=data.get("error"))
# Do NOT deduct credits on failure
return
Deducting credits after completion — not at submission — avoids charging users for failed generations.
How Do You Track Per-User AI Generation Costs?
You need per-user COGS visibility to price your tiers correctly and catch accounts that are disproportionately expensive to serve. A minimal schema:
CREATE TABLE ai_jobs (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
job_id TEXT NOT NULL UNIQUE,
model TEXT NOT NULL,
duration_s INTEGER,
resolution TEXT DEFAULT '720p',
cost_usd NUMERIC(10, 6),
status TEXT, -- queued | processing | done | failed
created_at TIMESTAMPTZ DEFAULT now()
);
-- Monthly COGS by user
SELECT
user_id,
SUM(cost_usd) AS total_spend_usd,
COUNT(*) FILTER (WHERE status = 'done') AS successful_jobs,
COUNT(*) FILTER (WHERE status = 'failed') AS failed_jobs
FROM ai_jobs
WHERE created_at >= date_trunc('month', now())
GROUP BY user_id
ORDER BY total_spend_usd DESC;
This table also lets you enforce per-plan credit limits at submission time — reject the job before you incur the cost, not after.
What Does COGS Look Like at Scale?
Assume each active user generates 10 clips per month at 5 seconds each (720p):
| Model | Cost/clip | 100 users/mo | 1,000 users/mo | 10,000 users/mo |
|---|---|---|---|---|
| Grok Imagine 1.0 | $0.05 | $50 | $500 | $5,000 |
| Gemini Omni Flash | $0.10 | $100 | $1,000 | $10,000 |
| Grok Imagine 1.5 | $0.11 | $110 | $1,100 | $11,000 |
| Veo 3.1 (720p) | $0.30 | $300 | $3,000 | $30,000 |
| Veo 3.1 (1080p) | $0.60 | $600 | $6,000 | $60,000 |
At 1,000 active users, even Veo 3.1 at $3,000/mo is manageable if your plan pricing reflects the quality tier. Offering Grok 1.0 on a free/starter tier and Veo 3.1 on a Pro tier lets you match COGS to revenue by plan.
GenRelay's free tier covers initial development and user testing; the $14/mo plan works for early-stage SaaS with low volume; pay-as-you-go scales linearly as you grow.
How Do You Handle Generation Failures and Retries?
Not all failures should be retried. Categorize before acting:
def handle_failure(job_id: str, error: str):
job = db.jobs.get(job_id=job_id)
policy_violation = "content_policy" in error.lower()
if policy_violation:
# Do not retry; notify user with the reason
db.jobs.update(job_id, status="failed_policy")
notify_user(job["user_id"], "Generation declined: content guidelines.")
else:
# Transient failure — retry once after delay
time.sleep(30)
new_id = submit_video_job(
job["user_id"], job["prompt"], job["model"], job["duration"]
)
db.jobs.update(job_id, status="retried", retry_job_id=new_id)
Always check for 429 (rate limit) responses at submission time and back off before the job even enters the queue. The async video API polling guide covers queue-based concurrency control.
FAQ
Can I let users choose between video models?
Yes, and it's a natural SaaS tier mechanism. Surface models as "Standard" (Grok 1.0), "Advanced" (Grok 1.5 or Omni Flash), and "Pro" (Veo 3.1). Gate each tier by plan, price the plans to cover COGS plus margin.
Should I cache generated videos?
Always. Copy output to your own S3/GCS bucket immediately — the GenRelay output URL is time-limited. Cache by a key of model + prompt hash + duration + resolution to avoid re-generating identical clips.
What concurrency limits apply?
Limits depend on your GenRelay plan. Use a job queue (Celery, BullMQ, SQS) to serialize submissions when concurrent jobs approach plan limits. Implement exponential backoff on 429 responses.
Is there a webhook option instead of polling?
Yes. Include a callback_url in your job submission payload and GenRelay will POST the completion event to your endpoint, eliminating polling overhead for high-volume SaaS workloads.
How do I handle the output URL expiry in a multi-region setup?
Download and re-upload to your CDN within 30 minutes of job completion. If you're running workers across regions, trigger the download from the worker that detected completion to minimize transfer latency.
Pricing as of August 2026. Visit genrelay.ai/pricing for current rates.