Pictory APIs provide a programmatic interface to create, edit, and render videos using AI-driven automation. These APIs are designed to transform textual content into engaging videos by automatically generating storyboards, applying voiceovers, and rendering professional-quality output. Below are some Node.js examples demonstrating how to render videos using the Pictory APIs.

Create Video from Text

This script demonstrates how to programmatically interact with the Pictory Video Generation API to create and render videos from text using Node.js

const axios = require('axios');

const { CLIENT_ID, CLIENT_SECRET, X_PICTORY_USER_ID } = process.env;

const API_ENDPOINT = 'https://api.pictory.ai';

const headers = (token) => ({
  Authorization: token,
  'X-Pictory-User-Id': X_PICTORY_USER_ID,
});

async function getAccessToken() {
  const res = await axios.post(`${API_ENDPOINT}/pictoryapis/v1/oauth2/token`, {
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
  });
  return res.data.access_token;
}

async function createStoryboard(token) {
  const res = await axios.post(
    `${API_ENDPOINT}/pictoryapis/v1/video/storyboard`,
    {
      videoName: 'InternationalYogaDay',
      videoWidth: 1920,
      videoHeight: 1080,
      language: 'en',
      audio: {
        autoBackgroundMusic: true,
        backGroundMusicVolume: 0.5,
        aiVoiceOvers: [
          {
            speaker: 'Aditi',
          },
          {
            speaker: 'Aveek',
          },
        ],
      },
      scenes: [
        {
          text: "Today, we come together to celebrate a very special occasion — International Yoga Day, observed every year on June 21st. Yoga is more than just a form of exercise. It is a way of life — a practice that connects the body, mind, and spirit. It brings peace, harmony, and balance to our lives. International Yoga Day was first proposed by India and adopted by the United Nations in 2014. Since then, millions across the globe roll out their mats each year to embrace this ancient practice. So today, let’s take a moment to breathe deeply, stretch our bodies, and calm our minds. Whether you're a beginner or a regular practitioner, yoga has something to offer everyone. Let’s celebrate this day by promoting health, mindfulness, and well-being — not just for ourselves, but for the world around us.",
          voiceOver: true,
          splitTextOnNewLine: false,
          splitTextOnPeriod: true,
        },
      ],
    },
    { headers: headers(token) }
  );

  return res.data.data.job_id;
}

async function pollStoryboardJobStatus(jobId, token) {
  const url = `${API_ENDPOINT}/pictoryapis/v1/jobs/${jobId}`;
  let renderParams = null;
  do {
    const res = await axios.get(url, { headers: headers(token) });
    renderParams = res.data.data.renderParams;
    if (renderParams) {
      return renderParams;
    }

    await new Promise((r) => setTimeout(r, 5000));
  } while (!renderParams);
}

async function pollRenderVideoJobStatus(jobId, token) {
  const url = `${API_ENDPOINT}/pictoryapis/v1/jobs/${jobId}`;
  let status = '';
  do {
    const res = await axios.get(url, { headers: headers(token) });
    status = res.data.data.status;
    if (status === 'completed') {
      return res.data.data;
    }
    await new Promise((r) => setTimeout(r, 5000));
  } while (status !== 'Failed');
}

async function renderVideo(token, storyboardJobId) {
  const res = await axios.put(
    `${API_ENDPOINT}/pictoryapis/v1/video/render/${storyboardJobId}`,
    {
      webhook: 'https://webhook.site/f24bfe6a-7065-4bd8-977c-52d184fc4374',
    },
    { headers: headers(token) }
  );
  return res.data.data.job_id;
}

(async () => {
    //Get access_token from token endpoint
  const token = await getAccessToken();

  //Create storyboard using text from storyboard endpoint
  const storyboardJobId = await createStoryboard(token);

  //Wait for the storyboard job to complete
  const renderParams = await Promise.any([
    pollStoryboardJobStatus(storyboardJobId, token),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('TIME_OUT');
      }, 30 * 1000); //30 second timeout
    }),
  ]);

  //Render storyboard to video using storyboard job ID from render endpoint
  const renderJobId = await renderVideo(token, storyboardJobId);

  //Wait for render job to complete
  const videoOutput = await Promise.any([
    pollRenderVideoJobStatus(renderJobId, token),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('TIME_OUT');
      }, 5 * 60 * 1000); //5 minutes timeout
    }),
  ]);

  //Rendered output
  console.log(videoOutput);
})();