Skip to main content

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

1

Use Concurrency

Process multiple videos in parallel (5-10 concurrent requests)
const concurrency = 5;
2

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);
});
3

Cache Templates

Fetch template details once and reuse
const template = await getTemplate(templateId);
// Reuse for all recipients
4

Monitor Rate Limits

Track and respect API rate limits
const rateLimiter = new RateLimiter(100, '1h');

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