TikTok Shop Product Video Automation (FFmpeg API)

March 17, 2026 · RenderIO

Manual product video creation doesn't scale

TikTok Shop product video automation fixes a real math problem. You have 500 products. Each needs a video. Your editor produces 8-10 per day. At that rate, you'll finish the catalog in two months. By then the first 100 need updating because prices changed.

The platform rewards fresh video content per product. Manual creation can't keep pace. A template-based automation pipeline can: one video template plus product data generates unlimited product videos.

The template approach

Instead of creating each video from scratch, you build templates:

  1. A 9:16 background video (15-30 seconds of branded motion graphics)

  2. A product image overlay positioned in the frame's product area

  3. A text overlay with product name, price, and key features

  4. An audio track (music or a voiceover template)

FFmpeg combines these into a finished product video. The RenderIO API runs FFmpeg at scale.

Building the template

Create a base template

Your template is a 1080x1920 video with designated areas for product content. Think of it as a video version of a graphic design template.

Example structure:

  • Top 400px: Brand header / hook text area

  • Middle 800px: Product showcase area

  • Bottom 720px: Price, CTA, features area

Overlay a product image

ffmpeg -i template.mp4 -i product-image.png \
  -filter_complex "[1:v]scale=600:-1[product];[0:v][product]overlay=(W-w)/2:400[v]" \
  -map "[v]" -map 0:a \
  -c:v libx264 -crf 20 -c:a copy \
  product-video.mp4

This scales the product image to 600px wide (maintaining aspect ratio) and centers it 400px from the top.

Add product text

ffmpeg -i template.mp4 -i product-image.png \
  -filter_complex "\
    [1:v]scale=600:-1[product];\
    [0:v][product]overlay=(W-w)/2:400,\
    drawtext=text='Premium Wireless Headphones':fontsize=42:fontcolor=white:x=(w-text_w)/2:y=1300:font=Arial:fontfile=/path/to/arial.ttf,\
    drawtext=text='$49.99':fontsize=56:fontcolor=#FF6B35:x=(w-text_w)/2:y=1400:font=Arial:fontfile=/path/to/arial-bold.ttf,\
    drawtext=text='Free Shipping • 30-Day Returns':fontsize=28:fontcolor=#CCCCCC:x=(w-text_w)/2:y=1500:font=Arial:fontfile=/path/to/arial.ttf[v]" \
  -map "[v]" -map 0:a \
  -c:v libx264 -crf 20 -c:a copy \
  product-video.mp4

To add a persistent brand logo to every video, see the FFmpeg watermark guide.

Add product clip instead of static image

If you have short product clips (from suppliers or AI generation):

ffmpeg -i template.mp4 -i product-clip.mp4 \
  -filter_complex "\
    [1:v]scale=600:-1[product];\
    [0:v][product]overlay=(W-w)/2:400:shortest=1[v]" \
  -map "[v]" -map 0:a -t 15 \
  -c:v libx264 -crf 20 -c:a copy \
  product-video.mp4

Automate with RenderIO API

Single product video

curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
  -H "X-API-KEY: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ffmpeg_command": "-i {{in_template}} -i {{in_product}} -filter_complex \"[1:v]scale=600:-1[product];[0:v][product]overlay=(W-w)/2:400,drawtext=text=Premium_Headphones:fontsize=42:fontcolor=white:x=(w-text_w)/2:y=1300,drawtext=text=$49.99:fontsize=56:fontcolor=#FF6B35:x=(w-text_w)/2:y=1400[v]\" -map \"[v]\" -map 0:a -c:v libx264 -crf 20 -c:a copy -movflags +faststart {{out_video}}",
    "input_files": {
      "in_template": "https://storage.example.com/templates/product-template-v1.mp4",
      "in_product": "https://storage.example.com/products/headphones.png"
    },
    "output_files": { "out_video": "headphones-tiktok.mp4" }
  }'

For API authentication, polling patterns, and the full response structure, see the FFmpeg API complete guide.

Batch process entire catalog

const products = [
  { name: "Wireless_Headphones", price: "$49.99", image: "https://cdn.example.com/headphones.png" },
  { name: "Phone_Case", price: "$19.99", image: "https://cdn.example.com/case.png" },
  { name: "USB-C_Cable", price: "$12.99", image: "https://cdn.example.com/cable.png" },
  // ... 500 more products
];

const TEMPLATE_URL = "https://storage.example.com/templates/product-template-v1.mp4";

async function generateProductVideos(products) {
  const jobs = products.map(product => {
    const command = `-i {{in_template}} -i {{in_product}} -filter_complex "[1:v]scale=600:-1[product];[0:v][product]overlay=(W-w)/2:400,drawtext=text='${product.name}':fontsize=42:fontcolor=white:x=(w-text_w)/2:y=1300,drawtext=text='${product.price}':fontsize=56:fontcolor=#FF6B35:x=(w-text_w)/2:y=1400[v]" -map "[v]" -map 0:a -c:v libx264 -crf 20 -c:a copy -movflags +faststart {{out_video}}`;

    return fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
      method: "POST",
      headers: {
        "X-API-KEY": process.env.RENDERIO_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        ffmpeg_command: command,
        input_files: {
          in_template: TEMPLATE_URL,
          in_product: product.image,
        },
        output_files: { out_video: `${product.name.toLowerCase()}-tiktok.mp4` },
      }),
    }).then(r => r.json());
  });

  return Promise.all(jobs);
}

// Process 500 products
const results = await generateProductVideos(products);
console.log(`Started ${results.length} video generation jobs`);

500 product videos, all processing in parallel. Each runs on the API independently.

For error handling and concurrent job management in production, the Node.js FFmpeg API guide has complete examples. For a no-code approach to the same pipeline, see the n8n RenderIO product videos guide.

Creating variations per product

One product video isn't enough. TikTok rewards volume. Create 3-5 variations per product:

const variations = [
  { suffix: "v1", filter: "eq=saturation=1.0" },
  { suffix: "v2", filter: "eq=saturation=1.2:contrast=1.05" },
  { suffix: "v3", filter: "hue=h=10,eq=saturation=1.1" },
  { suffix: "v4", filter: "eq=brightness=0.03:contrast=1.08" },
  { suffix: "v5", filter: "colorbalance=rs=0.04:gs=-0.01:bs=-0.03" },
];

async function createProductVariations(productName, baseVideoUrl) {
  const jobs = variations.map(v =>
    fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
      method: "POST",
      headers: {
        "X-API-KEY": process.env.RENDERIO_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        ffmpeg_command: `-i {{in_video}} -vf "${v.filter}" -c:v libx264 -crf 22 -c:a copy {{out_video}}`,
        input_files: { in_video: baseVideoUrl },
        output_files: { out_video: `${productName}-${v.suffix}.mp4` },
      }),
    }).then(r => r.json())
  );

  return Promise.all(jobs);
}

500 products x 5 variations = 2,500 unique TikTok videos. All automated.

Pipeline costs

ScaleVideos/monthAPI callsPlanCost
Small catalog100 products x 3 variations400Growth$29/mo
Medium catalog500 products x 5 variations3,000Business$99/mo
Large catalog2,000 products x 5 variations12,000Business$99/mo
Enterprise10,000 products x 5 variations60,000Business$99/mo + overage

Business plan overage is just 0.02/command.Comparetohiringavideoeditorat0.02/command. Compare to hiring a video editor at 3,000-5,000/month who produces 200 videos. The API approach is 10-100x cheaper and infinitely faster.

Automating TikTok Shop at scale? The Business plan at 99/mocovers20,000commandswith99/mo covers 20,000 commands with 0.02/command overage. Explore our video automation API or get your API key and start processing product videos today.

Webhook-based completion handling

Polling GET /api/v1/commands/:commandId works for one-off jobs. For a production pipeline processing hundreds of videos, webhooks are cleaner. When a job finishes, the API posts the result directly to your server:

const response = await fetch("https://renderio.dev/api/v1/run-ffmpeg-command", {
  method: "POST",
  headers: {
    "X-API-KEY": process.env.RENDERIO_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    ffmpeg_command: `-i {{in_template}} -i {{in_product}} -filter_complex "[1:v]scale=600:-1[product];[0:v][product]overlay=(W-w)/2:400,drawtext=text='${product.name}':fontsize=42:fontcolor=white:x=(w-text_w)/2:y=1300[v]" -map "[v]" -map 0:a -c:v libx264 -crf 20 -c:a copy -movflags +faststart {{out_video}}`,
    input_files: { in_template: TEMPLATE_URL, in_product: product.image },
    output_files: { out_video: `${product.id}.mp4` },
    webhook_url: "https://yourserver.com/webhooks/renderio",
  }),
});

Your endpoint receives a POST with the completed job details and output URL. No polling loop. The Node.js FFmpeg API guide has a full webhook handler with signature verification.

Integration with TikTok Shop

Once RenderIO delivers the video, upload it to TikTok Shop via their Seller API:

  1. RenderIO processes the video and calls your webhook

  2. Your server downloads the video from the RenderIO storage URL

  3. Your server uploads the file to TikTok Shop's video endpoint

  4. TikTok returns a video_id to attach to the product listing

async function uploadToTikTokShop(videoUrl, productId) {
  // Download from RenderIO
  const videoBuffer = await fetch(videoUrl).then(r => r.arrayBuffer());

  // Upload to TikTok Shop
  const formData = new FormData();
  formData.append('file', new Blob([videoBuffer], { type: 'video/mp4' }), 'product.mp4');

  const uploadRes = await fetch('https://open-api.tiktokshop.com/product/202309/videos/upload', {
    method: 'POST',
    headers: { 'x-tts-access-token': process.env.TIKTOK_ACCESS_TOKEN },
    body: formData,
  });

  const { data } = await uploadRes.json();
  return data.video_id;
}

The full Seller API flow (OAuth token refresh, product listing updates) is outside the scope of this guide. Full documentation is at the TikTok for Developers portal. New product added to your catalog? The webhook-driven flow means video creation and upload happen automatically within minutes.

Troubleshooting template issues

Text renders off-screen: The drawtext filter uses absolute pixel coordinates. On a 1080x1920 canvas, y=1300 works. On a different template resolution, those coordinates shift. Test with one product before running the full catalog.

Font not found: FFmpeg's drawtext requires an explicit font path. If you see No such file or directory for a font, use the full system path: /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf on Ubuntu, /System/Library/Fonts/Helvetica.ttc on macOS. Pass it as fontfile=/path/to/font.ttf.

Product image bleeds past the overlay area: scale=600:-1 doesn't constrain height. A tall portrait image can extend past the product area. Add a height limit: scale=600:800:force_original_aspect_ratio=decrease to keep images within a defined box.

Audio missing from output: If you're using -map "[v]" -map 0:a, verify the template has an audio stream. Run ffprobe -show_streams template.mp4 and check for codec_type=audio. If the template is video-only, add a separate audio file with -i music.mp3 and map it explicitly.

Blank output for some products: Some product images may have malformed headers or unexpected pixel formats. Run ffprobe -show_streams product.png on the failing image. Add a fallback placeholder image to prevent one bad asset from stalling the whole batch.

FAQ

How many product videos can I generate per day?

No hard daily limit. Rate limits depend on your plan: Growth covers 1,000 commands/month, Business covers 20,000/month with $0.02/command overage. Submitting 500 jobs in parallel works fine. The FFmpeg API complete guide covers rate limits and concurrent job management.

Do I need a TikTok Shop seller account?

Yes. This guide handles video creation and delivery. Uploading to TikTok Shop requires a seller account with API access. Apply through the TikTok for Developers portal for Seller API credentials.

What video format does TikTok Shop require?

MP4 or MOV, H.264 codec, 1080x1920 resolution (9:16), under 100MB per video. Keep videos 15-60 seconds for product showcase content. The commands in this guide produce MP4 with H.264, which passes TikTok's requirements.

Can I use supplier images directly?

Yes, as long as they're accessible via URL. Pass the image URL as in_product in the API request. If images sit behind authentication or a private CDN, download them first and host them somewhere publicly accessible before submitting.

How do I handle products with no images?

Add a check before submitting. If product.image is null or empty, skip that product or substitute a generic placeholder. A fallback prevents the API call from failing on missing assets and keeps the batch output predictable.


The batch job above submits all jobs in parallel. API processing runs concurrently, so a 500-product catalog doesn't take 500x the time of one video. Start with a small test batch of 5-10 products, verify the output looks right, then run the full catalog.

For related batch video workflows, see the batch process AI videos for social media guide.