Documentation Index
Fetch the complete documentation index at: https://docs.slidevid.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Bulk generation allows you to create hundreds or thousands of personalized videos efficiently using templates and automation.Use Case: Perfect for personalized marketing campaigns, sales outreach, customer onboarding, and more.
Prerequisites
Before bulk generating videos:You need:
- A template with defined variables
- A list of recipients with their data
- A webhook endpoint to receive completion notifications
- Sufficient API credits/quota
Basic Bulk Generation
Step 1: Prepare Your Data
// recipients.json
const recipients = [
{
name: "Sarah Johnson",
company: "TechCorp",
role: "CEO",
metric: "30% increase in productivity"
},
{
name: "Mike Chen",
company: "StartupXYZ",
role: "CTO",
metric: "50% faster deployment"
},
{
name: "Emma Davis",
company: "BigCorp Inc",
role: "VP of Sales",
metric: "$2M in additional revenue"
}
// ... more recipients
];
Step 2: Simple Bulk Generator
async function bulkGenerate(templateId, recipients) {
const results = [];
for (const recipient of recipients) {
try {
const response = await fetch('https://api.slidevid.ai/v1/project/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.SLIDEVID_API_KEY
},
body: JSON.stringify({
type: 'class',
templateId: templateId,
title: `Video for ${recipient.name}`,
scenes: [{
script: template.scenes[0].script,
variables: Object.entries(recipient).map(([key, value]) => ({
key,
value: String(value)
}))
}],
webhook: `https://yoursite.com/webhook?recipient=${recipient.email}`
})
});
const result = await response.json();
if (result.success) {
results.push({
recipient: recipient.name,
projectId: result.data.projectId,
status: 'queued'
});
console.log(`✅ Queued video for ${recipient.name}`);
} else {
throw new Error(result.message);
}
// Rate limiting: wait 1 second between requests
await new Promise(r => setTimeout(r, 1000));
} catch (error) {
console.error(`❌ Failed for ${recipient.name}:`, error.message);
results.push({
recipient: recipient.name,
status: 'failed',
error: error.message
});
}
}
return results;
}
// Execute
const results = await bulkGenerate('template_123', recipients);
console.log(`Generated ${results.filter(r => r.status === 'queued').length}/${recipients.length} videos`);
Advanced Bulk Generation
With Rate Limiting & Retries
class BulkVideoGenerator {
constructor(apiKey, options = {}) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.slidevid.ai/v1';
this.concurrency = options.concurrency || 5;
this.retryAttempts = options.retryAttempts || 3;
this.retryDelay = options.retryDelay || 5000;
}
async generateOne(templateId, recipient, attempt = 1) {
try {
const response = await fetch(`${this.baseUrl}/project/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': this.apiKey
},
body: JSON.stringify({
type: 'class',
templateId,
title: `Video for ${recipient.name}`,
scenes: [{
variables: Object.entries(recipient).map(([key, value]) => ({
key,
value: String(value)
}))
}],
webhook: recipient.webhook
})
});
const result = await response.json();
if (!response.ok) {
// Retry on rate limit
if (response.status === 429 && attempt < this.retryAttempts) {
const retryAfter = response.headers.get('Retry-After') || this.retryDelay / 1000;
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
return this.generateOne(templateId, recipient, attempt + 1);
}
throw new Error(result.message);
}
return {
success: true,
recipient: recipient.name,
projectId: result.data.projectId
};
} catch (error) {
if (attempt < this.retryAttempts) {
console.log(`Retry ${attempt}/${this.retryAttempts} for ${recipient.name}`);
await new Promise(r => setTimeout(r, this.retryDelay));
return this.generateOne(templateId, recipient, attempt + 1);
}
return {
success: false,
recipient: recipient.name,
error: error.message
};
}
}
async generateBulk(templateId, recipients, onProgress) {
const results = [];
const batches = [];
// Split into batches based on concurrency
for (let i = 0; i < recipients.length; i += this.concurrency) {
batches.push(recipients.slice(i, i + this.concurrency));
}
let completed = 0;
for (const batch of batches) {
const batchResults = await Promise.all(
batch.map(recipient => this.generateOne(templateId, recipient))
);
results.push(...batchResults);
completed += batch.length;
if (onProgress) {
onProgress({
completed,
total: recipients.length,
percentage: Math.round((completed / recipients.length) * 100)
});
}
// Wait between batches to respect rate limits
if (batches.indexOf(batch) < batches.length - 1) {
await new Promise(r => setTimeout(r, 2000));
}
}
return results;
}
getStats(results) {
return {
total: results.length,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
successRate: Math.round((results.filter(r => r.success).length / results.length) * 100)
};
}
}
// Usage
const generator = new BulkVideoGenerator(process.env.SLIDEVID_API_KEY, {
concurrency: 5,
retryAttempts: 3,
retryDelay: 5000
});
const results = await generator.generateBulk(
'template_123',
recipients,
(progress) => {
console.log(`Progress: ${progress.completed}/${progress.total} (${progress.percentage}%)`);
}
);
const stats = generator.getStats(results);
console.log('Generation complete:', stats);
Processing CSV Files
Read and Process CSV
import fs from 'fs';
import csv from 'csv-parser';
async function processCSV(filePath, templateId) {
const recipients = [];
return new Promise((resolve, reject) => {
fs.createReadStream(filePath)
.pipe(csv())
.on('data', (row) => {
// Validate required fields
if (row.name && row.email) {
recipients.push({
name: row.name,
email: row.email,
company: row.company || 'Unknown',
role: row.role || 'Professional',
// Add more fields as needed
webhook: `https://yoursite.com/webhook?email=${row.email}`
});
}
})
.on('end', async () => {
console.log(`Loaded ${recipients.length} recipients from CSV`);
const generator = new BulkVideoGenerator(process.env.SLIDEVID_API_KEY);
const results = await generator.generateBulk(templateId, recipients);
resolve(results);
})
.on('error', reject);
});
}
// Usage
const results = await processCSV('./recipients.csv', 'template_123');
Database Integration
Generate from Database Records
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function generateFromDatabase(templateId, filters = {}) {
// Fetch recipients from database
const contacts = await prisma.contact.findMany({
where: {
status: 'active',
hasConsent: true,
...filters
},
select: {
id: true,
name: true,
email: true,
company: true,
customFields: true
}
});
console.log(`Found ${contacts.length} contacts in database`);
const generator = new BulkVideoGenerator(process.env.SLIDEVID_API_KEY);
const results = await generator.generateBulk(
templateId,
contacts.map(c => ({
...c,
...c.customFields,
webhook: `https://yoursite.com/webhook?contactId=${c.id}`
})),
async (progress) => {
// Update progress in database
await prisma.bulkJob.update({
where: { id: jobId },
data: {
progress: progress.percentage,
completed: progress.completed
}
});
}
);
// Save results to database
for (const result of results) {
if (result.success) {
await prisma.video.create({
data: {
projectId: result.projectId,
contactId: result.recipient.id,
status: 'processing'
}
});
}
}
return results;
}
const results = await generateFromDatabase('template_123', {
segment: 'enterprise'
});
Webhook Management for Bulk Jobs
Track Completions
// In-memory tracking (use Redis/Database for production)
const bulkJobs = new Map();
// Start bulk job
app.post('/api/bulk/start', async (req, res) => {
const { templateId, recipients } = req.body;
const jobId = `job_${Date.now()}`;
bulkJobs.set(jobId, {
id: jobId,
total: recipients.length,
completed: 0,
videos: [],
startedAt: new Date()
});
// Start generation in background
generateBulkAsync(jobId, templateId, recipients);
res.json({ jobId, status: 'started' });
});
// Webhook handler
app.post('/api/webhook', async (req, res) => {
const { projectId, status, video } = req.body;
const jobId = req.query.jobId;
if (jobId && bulkJobs.has(jobId)) {
const job = bulkJobs.get(jobId);
job.completed++;
if (status === 'completed') {
job.videos.push({
projectId,
url: video.url,
completedAt: new Date()
});
}
// Check if job complete
if (job.completed === job.total) {
job.completedAt = new Date();
job.duration = job.completedAt - job.startedAt;
console.log(`Bulk job ${jobId} completed!`);
console.log(`Generated ${job.videos.length}/${job.total} videos in ${job.duration}ms`);
// Notify user
await notifyJobComplete(jobId, job);
}
bulkJobs.set(jobId, job);
}
res.status(200).send('OK');
});
// Check job status
app.get('/api/bulk/:jobId', (req, res) => {
const job = bulkJobs.get(req.params.jobId);
if (!job) {
return res.status(404).json({ error: 'Job not found' });
}
res.json({
...job,
progress: Math.round((job.completed / job.total) * 100),
status: job.completedAt ? 'completed' : 'processing'
});
});
Performance Optimization
Tips for Large Batches
Use Concurrency
Process multiple videos in parallel (5-10 concurrent requests)
const concurrency = 5;
Implement Queuing
Use a job queue (Bull, BullMQ) for reliability
import Queue from 'bull';
const videoQueue = new Queue('videos', {
redis: { host: 'localhost', port: 6379 }
});
videoQueue.process(async (job) => {
return await generateVideo(job.data);
});
Cache Templates
Fetch template details once and reuse
const template = await getTemplate(templateId);
// Reuse for all recipients
Error Handling Strategies
Partial Failure Recovery
async function generateWithRecovery(templateId, recipients) {
const results = {
successful: [],
failed: [],
pending: [...recipients]
};
// Save state to disk/database
const saveState = () => {
fs.writeFileSync('bulk-state.json', JSON.stringify(results));
};
for (const recipient of recipients) {
try {
const projectId = await generateOne(templateId, recipient);
results.successful.push({ recipient, projectId });
results.pending = results.pending.filter(r => r !== recipient);
} catch (error) {
results.failed.push({ recipient, error: error.message });
results.pending = results.pending.filter(r => r !== recipient);
}
// Save progress
saveState();
}
return results;
}
// Resume from saved state
function resumeBulkGeneration() {
const state = JSON.parse(fs.readFileSync('bulk-state.json'));
return generateWithRecovery(templateId, state.pending);
}
Best Practices
Start Small
Test with 5-10 videos before scaling to hundreds
Use Webhooks
Never poll for bulk jobs - always use webhooks
Monitor Progress
Track success rates and errors in real-time
Implement Retries
Handle transient failures with exponential backoff
Example: Complete Bulk Campaign
// complete-campaign.js
import { BulkVideoGenerator } from './bulk-generator';
import { sendEmail } from './email-service';
async function runCampaign(campaignId) {
// 1. Load recipients from database
const recipients = await db.recipients.findMany({
where: { campaignId, status: 'pending' }
});
// 2. Get template
const campaign = await db.campaign.findUnique({
where: { id: campaignId },
include: { template: true }
});
// 3. Generate videos
const generator = new BulkVideoGenerator(process.env.SLIDEVID_API_KEY);
const results = await generator.generateBulk(
campaign.template.id,
recipients.map(r => ({
...r,
webhook: `https://mysite.com/webhook?recipientId=${r.id}&campaignId=${campaignId}`
})),
async (progress) => {
// Update campaign progress
await db.campaign.update({
where: { id: campaignId },
data: { progress: progress.percentage }
});
}
);
// 4. Log results
const stats = generator.getStats(results);
await db.campaign.update({
where: { id: campaignId },
data: {
status: 'completed',
stats: stats,
completedAt: new Date()
}
});
// 5. Notify admin
await sendEmail({
to: campaign.owner.email,
subject: `Campaign ${campaign.name} completed`,
text: `Generated ${stats.successful}/${stats.total} videos (${stats.successRate}% success rate)`
});
return results;
}
// Start campaign
runCampaign('campaign_123');
Next Steps
Personalization Tips
Advanced personalization techniques
Best Practices
Production-ready patterns