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

How-to

Automated News Video Generation: One Edition Every Morning

Render a daily news and weather brief from feeds instead of from a script. One edition a morning, 26 fields, one credit, and nothing to review before it ships.

Automated News Video Generation: One Edition Every Morning

A morning brief is data, not writing: today's temperature, a three-day outlook, and three headlines. Bind those 26 values to a fixed layout, render one edition on a cron before the office opens, and publish it without a review step. Each 38-second edition costs one credit.

The finished video

This guide renders News & Weather - Daily Morning Brief - a 38-second 16:9 edition in seven scenes: a "Good morning" open carrying the date, today's weather over a full-bleed backdrop, a three-day forecast row, the day's top three stories in three different layouts, and a sign-off.

The 25 August edition, rendered from the payload in step 4.

Twenty-six fields change every morning. Fifty-six overlays are driven by them.

What you need

  • A Renderly API key. Create one in the dashboard under Settings → API keys.
  • A weather feed with current conditions and a three-day outlook.
  • A headline source: your own CMS, a wire, or an RSS feed you already publish.
  • A scheduler that fires once a day.

Step 1 - Decide whether your brief is data or a script

Search this and the results sell you an AI news generator: paste a story, get an avatar reading it. That is a different product, and the difference decides whether the pipeline can run unattended.

A generated brief is writing. A model drafts a script, a synthetic presenter performs it, and the output is a new video every day - which means somebody has to watch it before it goes out, because nobody ships an unreviewed machine-written script under a masthead.

A template brief is binding. The layout, the timing and the words around the data are fixed and were approved once. The only thing that changes at 6am is 26 values that came from feeds you already trust. There is nothing new to review, so the job can publish on its own, and that is the whole reason to automate it.

Pick the template route when the brief is the same shape every day. Pick a generator when each edition genuinely needs different writing - at which point you have a video production problem, not a scheduling one.

Step 2 - Pull the morning's numbers from a weather feed

Seventeen of the 26 fields are weather. They map onto a normal feed response, but three of them are formatted strings rather than raw numbers, and a feed will not guess the format:

FieldValueNote
current_temp68the number alone, no degree sign
current_conditionPartly Cloudy
current_high_lowH 74° • L 55°one string, both figures, you compose it
forecast_1_high76°degree sign, no label
forecast_1_lowLow 57°label and degree sign
forecast_1_condSunny

forecast_N_high and forecast_N_low are deliberately asymmetric - the high is the big 150px numeral and the low is the small line under it, so the word "Low" lives in the value rather than in the layout. Format both in the mapping function and write one test for it; this is the field pair that ships wrong.

Step 3 - Budget the three headlines before the feed picks them

The three story scenes use three different layouts, and their headline boxes are not the same size:

FieldBoxTypeLines
news_1_headline728x50088px5
news_2_headline788x44088px4
news_3_headline1588x21096px2
The three story scenes: a narrow headline column, a mirrored one, and a wide two-line headline over a full-bleed photograph
The three story scenes from the render above. Same type size, three different boxes.

The third is the trap. It is by far the widest box and by far the least forgiving, because a full-bleed layout gives the headline only two lines at 96px. The template's own three headlines run 62, 58 and 61 characters, and the boxes were drawn around that. Treat roughly 60 characters as the budget, cap it in the query that selects the stories, and put the shortest headline of the three in slot three.

An over-long headline does not fail the render. It renders, the last line falls out of the box, and the job reports success.

Step 4 - Render one edition on a morning cron

The assembled object goes out as one request. Note the template ID: news-weather-updates is the API identifier, and news-weather-daily-brief is only the gallery URL.

curl -X POST https://renderly.video/api/v1/renders \
  -H "Authorization: Bearer $RENDERLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "news-weather-updates",
    "replacements": {
      "edition_date": "Tuesday, August 25",
      "weather_backdrop": "https://cdn.example.com/sky/overcast.jpg",
      "current_temp": "68",
      "current_condition": "Partly Cloudy",
      "current_high_low": "H 74°  •  L 55°",
      "forecast_1_day": "Wed",
      "forecast_1_high": "76°",
      "forecast_1_low": "Low 57°",
      "forecast_1_cond": "Sunny",
      "news_1_category": "World",
      "news_1_headline": "City leaders approve sweeping transit plan to cut commute times",
      "news_1_image": "https://cdn.example.com/stories/transit.jpg"
    },
    "webhookUrl": "https://your-app.com/hooks/renderly"
  }'

The job around it is a mapping function and a schedule:

// 05:30 local, once a day.
const [weather, stories] = await Promise.all([
  weatherFeed.today(),
  cms.topStories({ limit: 3, maxHeadlineLength: 60 }),
]);
 
await fetch("https://renderly.video/api/v1/renders", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RENDERLY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    templateId: "news-weather-updates",
    replacements: {
      edition_date: formatEditionDate(new Date()),
      ...toWeatherFields(weather),
      ...toStoryFields(stories),
    },
    webhookUrl: "https://your-app.com/hooks/renderly",
  }),
});

maxHeadlineLength in the query is the whole of step 3, enforced at the only place it can be enforced cheaply. Any field left out of replacements keeps the template's default, so a feed that returns nothing publishes yesterday's sample weather rather than failing.

Step 5 - Publish the edition before the news goes stale

Because webhookUrl was included, Renderly posts back when the file is ready:

{
  "event": "render.completed",
  "data": {
    "jobId": "cm9x2k4p10001qw8r7a3b2c1d",
    "status": "COMPLETED",
    "outputUrl": "https://cdn.renderly.video/renders/cm9x2k4p10001qw8r7a3b2c1d.mp4",
    "creditsUsed": 1,
    "durationInFrames": 1140,
    "width": 1920,
    "height": 1080,
    "fps": 30
  },
  "timestamp": "2026-08-25T05:31:07.902Z"
}

Push outputUrl straight to the site, the newsletter or the channel. There is no approval queue in this pipeline by design: the layout was signed off once, and the only new material is the data. Payloads are signed with HMAC-SHA256, so 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. A 38-second edition rounds to one credit.

Editions per monthCreditsPlan
30 - one market, daily30Creator, $29/mo
150 - five markets, daily150Creator, $29/mo
300 - ten markets, daily300Creator, $29/mo
1,500 - fifty markets, daily1,500Business, $99/mo

Ten markets, every morning, all year, fits inside the $29 plan at roughly ten cents an edition. The interesting number is the last row: a fifty-market daily video brief costs $99 a month to render, which is less than one freelance edit, and the marginal cost of the fifty-first market is a row in a config file.

Where to go next

The pattern here is a schedule with a feed behind it. The TikTok guide runs the same cron shape against a booking system rather than a weather API, and the Google Sheets guide covers the case where a person, not a feed, fills the row.

If step 1 is still the open question, the long version of that argument - with the failure modes of each approach - is in template-based video vs AI-generated video.

Frequently asked

Why use a template instead of an AI news video generator?
Because a morning brief is data, not writing. The temperature, the three-day outlook and the day's headlines all arrive as strings from feeds you already trust, and the job is to put them in fixed slots. A generator writes a script and performs it, which means every edition is a new video that somebody has to watch before it ships. A template produces the same brief every day with today's numbers in it, so there is nothing to review and the cost per edition does not move.
What happens on a day with only two stories?
Omitting news_3_category, news_3_headline and news_3_image does not remove the scene - an omitted field keeps the template's default, so you would publish the sample story. Either fill the third slot with a lighter item, which is what a real bulletin does, or drop the trailing scene by editing the overlay list in inputProps rather than through replacements.
Can one job render several cities?
Yes, and the field split makes it cheap to do. The three headlines are usually national and the seventeen weather fields are local, so one query for the news and one query per market gives you an edition per city from the same loop. Ten markets is ten renders, ten credits and one morning job.
How long does an edition take to render?
The composition is 38 seconds at 1920x1080, and renders run in parallel on cloud infrastructure rather than on your machine. Schedule the job far enough ahead of publication that a retry still lands on time; the render.completed webhook tells you when the file is actually ready, so nothing downstream has to poll.
Does the temperature field handle three digits?
Check it before summer. current_temp is set at 340px inside a 460px box, which is drawn around two digits, so a three-digit reading is the first thing to test if you run this in a hot market. The same caution applies to any market that reports in Fahrenheit and crosses 100.