AI video generation is no longer a novelty — it's becoming a core feature in SaaS products. But integrating it into a production application comes with real engineering challenges: asynchronous processing, credit-based billing, cost management, and user experience design.
In this article, I'll walk through how I integrated Seedance 2.0 Mini into VEIGO's platform, built a credit system around it, and what I learned about pricing AI-generated content.
The Challenge
I needed to add AI video generation to a SaaS platform with these requirements:
- Users should be able to generate videos from text prompts or images
- The system needs to handle long-running generation tasks (30s to 5min)
- Monetization through a credit/point system rather than per-video pricing
- Seamless UX — users shouldn't feel like they're waiting for a batch job
Why Seedance 2.0 Mini
After evaluating Runway, Pika, Kling, and Stable Video Diffusion, I chose Seedance 2.0 Mini for several reasons:
- Cost-effectiveness: Competitive pricing for the quality output
- API reliability: Consistent uptime and response times
- Resolution flexibility: Multiple output options (480p, 720p, 1080p)
- Image-to-video capability: Critical for property listings and product showcases
- Chinese market alignment: Better support for Chinese text prompts and cultural context
Architecture: The Async Task Pattern
Video generation isn't instant. Even "fast" models take 30+ seconds. Here's the architecture I implemented:
User Request → Task Queue → Seedance API → Polling Service → Result Storage → User Notification
Step 1: Task Creation
When a user submits a generation request, we don't wait for the video. Instead:
// app/api/studio/generate/route.ts
export async function POST(req: Request) {
const { prompt, imageUrl, resolution, duration } = await req.json();
// Calculate credit cost based on parameters
const creditCost = calculateCredits(resolution, duration);
// Check user balance
const user = await getUserWithBalance(session.userId);
if (user.credits < creditCost) {
return Response.json({ error: "Insufficient credits" }, { status: 402 });
}
// Create task record
const task = await db.videoTask.create({
data: {
userId: session.userId,
status: "PENDING",
prompt,
imageUrl,
resolution,
duration,
creditsCharged: creditCost,
}
});
// Submit to Seedance API (non-blocking)
const seedanceTaskId = await submitToSeedance({
prompt,
imageUrl,
resolution,
duration
});
// Update task with external ID
await db.videoTask.update({
where: { id: task.id },
data: { externalTaskId: seedanceTaskId, status: "PROCESSING" }
});
return Response.json({
taskId: task.id,
creditsCharged: creditCost,
estimatedTime: getEstimate(duration)
});
}
Step 2: Polling Service
A background service polls the Seedance API for task completion:
// lib/services/video-poller.ts
export async function pollTaskCompletion() {
const pendingTasks = await db.videoTask.findMany({
where: { status: "PROCESSING" },
take: 10 // Batch processing
});
for (const task of pendingTasks) {
try {
const result = await seedance.getTaskStatus(task.externalTaskId);
if (result.status === "COMPLETED") {
// Download and store the video
const videoUrl = await downloadAndStore(result.videoUrl, task.id);
await db.videoTask.update({
where: { id: task.id },
data: {
status: "COMPLETED",
videoUrl,
completedAt: new Date()
}
});
// Notify user
await notifyUser(task.userId, {
type: "VIDEO_READY",
taskId: task.id
});
} else if (result.status === "FAILED") {
// Refund credits on failure
await refundCredits(task.userId, task.creditsCharged);
await db.videoTask.update({
where: { id: task.id },
data: { status: "FAILED", failureReason: result.error }
});
}
// If still processing, just wait for next poll cycle
} catch (error) {
console.error(`Poll error for task ${task.id}:`, error);
}
}
}
Step 3: User-Facing Status
The frontend polls for task status with a progress indicator:
// components/VideoTaskStatus.tsx
export function VideoTaskStatus({ taskId }: { taskId: string }) {
const [status, setStatus] = useState<TaskStatus>("PROCESSING");
const [progress, setProgress] = useState(0);
useEffect(() => {
const interval = setInterval(async () => {
const res = await fetch(`/api/studio/task/${taskId}`);
const data = await res.json();
setStatus(data.status);
setProgress(data.progress);
if (data.status === "COMPLETED" || data.status === "FAILED") {
clearInterval(interval);
}
}, 3000); // Poll every 3 seconds
return () => clearInterval(interval);
}, [taskId]);
return (
<div className="task-status">
<ProgressBar value={progress} />
<StatusBadge status={status} />
{status === "COMPLETED" && <VideoPlayer url={status.videoUrl} />}
</div>
);
}
The Credit System Design
Instead of per-video pricing (which feels unpredictable), I designed a credit bundle system:
| Plan | Credits | Price | Per-Credit Cost |
|---|---|---|---|
| Starter | 100 | $5 | $0.050 |
| Professional | 400 | $15 | $0.038 |
| Business | 1,000 | $30 | $0.030 |
| Enterprise | 2,500 | $50 | $0.020 |
Credit Cost Per Generation
Different parameters consume different credit amounts:
function calculateCredits(resolution: string, duration: number): number {
const baseCost = {
"480p": 2,
"720p": 4,
"1080p": 8
};
const durationMultiplier = duration <= 5 ? 1 : duration <= 10 ? 1.5 : 2;
return Math.ceil(baseCost[resolution] * durationMultiplier);
}
Why Credits Work Better Than Per-Video Pricing
- Predictable revenue: Users buy bundles upfront
- Flexible usage: A credit can generate a 480p clip or contribute toward a 1080p video
- Reduced decision fatigue: Users don't calculate cost per video
- Upsell opportunity: Running low? Here's a bigger bundle at a better rate
- Graceful degradation: If API costs change, adjust credit costs without changing user-facing prices
Pricing Strategy Thoughts
After 3 months of operation, here's what I've observed:
The $5 tier is the acquisition engine. Most users start here. It's low-risk enough to try, and 100 credits let them generate ~25 480p videos or ~12 720p videos.
The $30 tier is the sweet spot. Users who find value quickly upgrade here. The per-credit savings (40% vs Starter) feels significant.
Enterprise users negotiate. At $50 for 2,500 credits, businesses generating content at scale often reach out for custom pricing.
Key insight: The pricing isn't really about the credits — it's about removing friction. Users who buy credits generate more content, which makes the platform stickier, which reduces churn.
Cost Management
Seedance API costs aren't trivial. Here's how I keep margins healthy:
- Resolution-tiered pricing: Higher resolution costs more credits, matching higher API costs
- Batch processing: Group API calls to reduce overhead
- Caching: Similar prompts with same seed get cached results
- Failure handling: Failed generations refund credits immediately (maintaining trust)
- Usage monitoring: Alert when a user's generation pattern suggests abuse
Lessons Learned
1. Set expectations early
Users need to know generation takes time. We show estimated wait times upfront and use optimistic UI updates.
2. Always refund on failure
Nothing destroys trust faster than charging credits for failed generations. We refund immediately and send a notification explaining why it failed.
3. Offer a free tier
We give 10 free credits on signup. This lets users experience the product before committing money. Conversion rate from free to paid is ~15%.
4. Monitor API costs vs. credit revenue
We track the ratio weekly. Target is 60%+ gross margin. If API costs spike, we adjust credit costs (not plan prices — that would break trust).
5. Content moderation is mandatory
AI-generated video can be misused. We run all prompts through a content filter before submission and have a reporting system for generated content.
Try It Out
The AI Video Studio is live and integrated into the VEIGO platform. It's particularly useful for:
- Real estate agents creating property walkthrough videos
- E-commerce sellers generating product demos
- Content creators producing short-form video content
- Marketers creating social media ads
🎬 AI Video Studio: shopveigo.com/studio
💳 Credit Plans: shopveigo.com/pricing
🏠 Full Platform: shopveigo.com
Have you integrated AI video generation into your product? What challenges did you face? Share your experience in the comments.
Top comments (0)