Practical guide

Bulk Video Production with Shotstack: From CSV to 100 Personalized Clips

Learn how to generate hundreds of personalized videos at scale using Shotstack’s template‑merge workflow. The guide covers template creation, CSV validation, API orchestration, and cost considerations for developers and content teams.

Illustration of a Shotstack template with merge fields feeding data from a CSV to produce multiple personalized videos

Creating a single video with Shotstack is straightforward, but scaling that process to dozens or hundreds of unique videos—each with its own product name, price, image, or brand color—requires a different approach. By separating the visual design into a reusable template and feeding it structured data from a CSV, developers can automate the rendering of personalized videos in a repeatable, cost‑aware pipeline. This article walks through the full workflow: building a template with merge fields, generating a sample dataset, validating rows, submitting one render request per row, and collecting the final URLs. It also discusses practical limits, pricing, and how to extend the pattern to multiple audiences or formats.

Context and practical value

The source explains how to bulk‑create personalized videos with Shotstack by building a reusable template with merge fields, generating a CSV dataset, validating rows locally, submitting one render request per row, and collecting URLs via a resumable manifest. It also covers cost, rate limits, and extending the pattern to multiple formats or audiences.

This article distills the tutorial into a concise, step‑by‑step guide, highlights key architectural decisions, and adds practical advice on rate‑limit pacing, error handling, and cost estimation. It also expands on how to extend the pattern to multi‑format and multi‑client scenarios, providing developers with a reusable framework for large‑scale video production.

Key takeaways

  • Use a single Shotstack template with merge fields to keep design consistent across all videos.
  • Validate CSV rows locally before making API calls to avoid wasted credits and failures.
  • Submit one render request per row; the API does not accept bulk CSV uploads.
  • Track render status with a resumable manifest to recover from interruptions.
  • Adjust request pacing to stay below Shotstack’s rate limits for both stage and production environments.

Why Bulk Rendering Matters

When a retailer has hundreds of products or a marketing team needs personalized onboarding videos for thousands of customers, editing each clip by hand is infeasible. Bulk rendering lets you keep a single approved design while injecting dynamic content, ensuring brand consistency and reducing manual effort.

Template‑Merge Architecture

The core idea is to separate the static visual layout from the changing data. A Shotstack template defines the timeline, tracks, and assets, and uses placeholders like {{PRODUCT_NAME}} or {{IMAGE_URL}}. The dataset—typically a CSV—provides the values for each placeholder per row. The API call POST /edit/{version}/templates/render accepts a template ID and a merge array; it does not accept an entire CSV, so the client must iterate over rows.

Building the Template

You can design the template visually in Shotstack Studio or write the JSON directly. The example template in the source uses rich‑text assets for price, headline, and product name, and an image asset for the product photo. Merge fields are placed wherever the content should change. The template also sets output format (mp4, 9:16 aspect ratio, 25 fps) and resolution (hd). Once created, the template ID is stored for later use.

Generating a Sample CSV

A helper script creates 100 rows of demo data, including edge cases such as long headlines, Unicode characters, and varying image URLs. The CSV has columns: row_id, product_name, headline, price, image_url, brand_color. The script writes the file to disk so it can be reused for validation and rendering.

Local Validation

Before hitting the API, the script checks each row for required fields, duplicate row_id, length limits (product_name ≤40, headline ≤55, price ≤20), HTTPS image URLs, and valid hex colors. If any row fails, the entire batch is aborted to prevent wasted credits. In production, you could normalize data or provide fallbacks instead of aborting.

Submitting Renders

The bulk-render script loops over the CSV rows, pacing requests (default 1 per second) to stay below Shotstack’s limits (150 requests/60s in stage, 300 in production). For each row it sends a POST request with the template ID and a merge array containing the row’s values. The response includes a render ID, which is stored in a local manifest file for later status checks.

Polling Status and Collecting URLs

After submission, the script can poll the API for each render’s status. When a render finishes, the script retrieves the hosted URL from the serve endpoint and updates the manifest. The manifest is a resumable JSON file, so you can stop and restart the process without losing progress.

Practical next steps

  1. Create a reusable Shotstack template with merge fields for all dynamic elements.
  2. Generate or import a CSV dataset, ensuring each row meets validation rules.
  3. Run a local validation script to catch errors before API calls.
  4. Submit one render request per CSV row, pacing requests to stay within rate limits.
  5. Poll the API for status, collect hosted URLs, and store results in a resumable manifest.

Limits and verification

  • The API does not accept bulk CSV uploads; the client must iterate over rows, which can be slow for very large datasets.
  • Rate limits differ between stage and production; exceeding them can cause temporary throttling.
  • Cost calculations are approximate; actual credits depend on output duration and any AI‑generated assets.

FAQ

Can I submit all 100 rows in a single API call?

No. Shotstack’s render endpoint accepts only one template ID and one merge array per request. You must loop over the CSV rows and submit a separate request for each.

What happens if a render fails?

The API returns a status of "failed" and an error message. Your script can retry failed renders or log them for manual inspection. The manifest records each render’s status so you can resume later.

How do I handle different output formats?

Create a separate template for each format (e.g., 16:9, 1:1) and include the appropriate template ID in the CSV. The rendering loop remains unchanged.