New: AI voiceover + auto-captions - script to captioned video in one API call. Read the announcement

How-to

Google Sheets to Video: Automate Video Creation from a Sheet

Turn spreadsheet rows into finished videos. Connect a Google Sheet to the Renderly API with Apps Script, map columns to variables, and render every row.

Google Sheets to Video: Automate Video Creation from a Sheet

A spreadsheet is already the fastest place to write a video brief: one row per video, one column per thing that changes. This guide connects that sheet to the Renderly API with a short Apps Script, so every row renders itself. Setup takes about 30 minutes. After that, a new row produces a new video.

The finished video

This guide renders the Content Recap template - a 34-second vertical recap of one written piece, built for Reels and Shorts. Nine fields change per row: headline, hero_image, point_1 to point_4, publication_name, article_url and qr_image. Watch the output on the template page before you build anything.

What you need

  • A Google Sheet with one row per video you want.
  • A Renderly API key. Create one in the dashboard under Settings → API keys.
  • A template with dynamic overlays. Any template on the templates page works.

Step 1 - Prepare the Google Sheet

Name every column after a template variable. Row 1 is the header row, and the script reads it, so column order does not matter - only the spelling does.

Add two more columns: status, holding pending, rendering or done, and video_url for the result.

headlinehero_imagepoint_1statusvideo_url
The state of video in 2026https://cdn.example.com/hero-1.jpg63% of marketers used AI tools this yearpending
Why templates beat promptshttps://cdn.example.com/hero-2.jpgA template render costs twenty centspending

Every URL you put in a cell must serve the file directly. This is where most Sheets pipelines break, because a Google Drive share link is not a file URL.

Step 2 - Get a Renderly API key and template ID

List the templates available to you and copy the id of the one you want:

curl https://renderly.video/api/v1/templates \
  -H "Authorization: Bearer $RENDERLY_API_KEY"

The response also lists the variables for each template. Those names are your column headers.

Store the key with PropertiesService, not in a cell:

PropertiesService.getScriptProperties()
  .setProperty("RENDERLY_API_KEY", "rnd_...");

Anyone with view access to the sheet can read a cell. Script properties are not part of the sheet, so a shared or published sheet does not leak the key.

Step 3 - Map columns to template variables

Renderly templates mark replaceable elements with isDynamic: true, and the name of each dynamic overlay is the key you send in the replacements object. Match those names to your headers one to one. Text and shape overlays take content; image, video and sound overlays take src.

One Sheets-specific detail: a line break inside a cell (Alt+Enter) survives into the video. Templates with fixed-size text boxes, Content Recap among them, use those breaks to control where a headline wraps.

Step 4 - Send one render request per row

One row becomes one POST. Paste this into Extensions → Apps Script:

const ENDPOINT = "https://renderly.video/api/v1/renders";
const TEMPLATE_ID = "content-recap";
const SKIP = ["status", "video_url"];
 
function renderPendingRows() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const rows = sheet.getDataRange().getValues();
  const header = rows.shift();
  const statusCol = header.indexOf("status") + 1;
  const key = PropertiesService.getScriptProperties()
    .getProperty("RENDERLY_API_KEY");
 
  rows.forEach((row, i) => {
    if (row[statusCol - 1] !== "pending") return;
 
    const replacements = {};
    header.forEach((name, c) => {
      if (!SKIP.includes(name) && row[c] !== "") {
        replacements[name] = String(row[c]);
      }
    });
 
    // Claim the row first, so a mid-run failure cannot double-render it.
    sheet.getRange(i + 2, statusCol).setValue("rendering");
    SpreadsheetApp.flush();
 
    const response = UrlFetchApp.fetch(ENDPOINT, {
      method: "post",
      contentType: "application/json",
      headers: { Authorization: "Bearer " + key },
      payload: JSON.stringify({
        templateId: TEMPLATE_ID,
        replacements: replacements,
        webhookUrl: "https://your-app.com/hooks/renderly",
      }),
    });
 
    const job = JSON.parse(response.getContentText()).data;
    Logger.log("%s -> %s", replacements.headline, job.jobId);
  });
}

The request returns a job ID immediately. Rendering happens asynchronously, so nothing blocks and a 500-row sheet does not hold the script open.

Run it from the editor once. Then attach a time-driven trigger, or an onEdit trigger if you want a row to render the moment someone marks it pending.

If you would rather write no code, the same three fields go into an HTTP request module in n8n, Make.com or Zapier.

Step 5 - Receive the finished video with a webhook

Because webhookUrl was included, Renderly posts back when the job finishes:

{
  "event": "render.completed",
  "data": {
    "jobId": "cm9x2k4p10001qw8r7a3b2c1d",
    "status": "COMPLETED",
    "outputUrl": "https://cdn.renderly.video/renders/cm9x2k4p10001qw8r7a3b2c1d.mp4",
    "creditsUsed": 1,
    "durationInFrames": 1020,
    "fps": 30
  },
  "timestamp": "2026-08-13T09:12:44.110Z"
}

Write outputUrl into the row's video_url cell and set status to done. The sheet is now the single record of what has rendered and what has not, which is the reason to keep the pipeline in the sheet at all.

Payloads are signed with HMAC-SHA256. Verify the signature before you trust the body - the webhooks guide covers the check.

What this costs at scale

Renderly bills 1 credit per minute of 1080p output, rounded up to the nearest half credit. Content Recap runs 34 seconds, so each row costs 1 credit.

Rows per monthCreditsPlanCost
100100Creator, $29/moincluded
1,0001,000Business, $99/moincluded
5,0005,000Business + 3,500 extra$99 + $280

Extra credits on the Business plan are $0.08 each, so a 5,000-video month runs about $379, or 7.6 cents per finished video.

Watch the rounding if volume is high. Anything from 31 to 60 seconds bills 1 credit, and anything up to 30 seconds bills half. Trimming a 34-second cut to 30 seconds halves the bill on every row.

Where to go next

Only Step 1 changes for a different source. The replacements object and the render call are identical, which is why the Airtable version of this guide shares Steps 2 through 5 with this one.

For the pattern behind the whole approach - one design, many payloads - read dynamic video templates.