Marketing real estate can be a complex process, as it often requires being available to multiple clients at once. Answering questions about the properties for sale can be a tedious task, but using a video listing can save time and money.

However, creating custom videos for each property in a real estate catalog can be time-consuming. Thankfully, the Shotstack API can be used to generate the necessary videos without the need for custom-made videos. Shotstack allows you to render the videos in the cloud using a JSON data specification which have customizable placeholders.

This tutorial will guide you through the process of using the Shotstack and Notion APIs to generate a real estate video for different properties using their street address.

Shotstack and Notion setup

Shotstack

Shotstack provides a cloud-based video editing API that uses JSON data to describe the arrangement of a video edit.

To get your Shotstack API key register for a free developer account and log in to get the API key. Shotstack provides two API keys, one for the staging environment and one for production. For this tutorial, you will use the staging API key.

Shotstack API key

Notion

You will need to store some sample data about the property information in a database. That is where Notion comes in. Notion is an all-in-one workspace where you can write, plan, collaborate and get organized. We will be creating a database with real estate data that can be accessed via an API endpoint.

Create a Notion account or login if you have an account.

In order to expose your databases in your workspace to the Notion API, head over to Notion integrations.

Set up your integration and choose the rules and permissions for the Notion API to access data in your database. Name your integration Real Estate Integration. Once you click on view integrations, you will see your Notion API Token.

Notion integration

Notion integration permission

Notion API key

Now you need to set your database in Notion. In Notion click Add a page on the homepage. Then create your database table. For this tutorial you can Duplicate this example database and rename the title to Real Estate. Now click the button on the top right, scroll to Add connections and chose the Notion integration Real Estate Integration as the connection.

Setting up the project

This project is a simple back-end application built using Node.js. Start by creating a new directory and changing to that directory by typing the following commands in the terminal:

mkdir shotstack-notion-integration
cd shotstack-notion-integration

Next, type in the command below within the terminal to initialize the Node.js app and create a package.json file:

npm init -y

Next, you need to add our only dependency, Axios to send HTTP requests. Use the following command to install the module:

npm install axios

Preparing the JSON video template

Shotstack uses a JSON template to specify how a video should look. We will use this real estate video listing template in this tutorial:

To use the template go into the Shotstack Studio and create a new template. The video editor provides a convenient way to design templates using a drag and drop interface. The interface lets you export JSON to use in your applications and workflows.

In the editor create a new template and open up the JSON view by clicking the JSON VIEW toggle:

Copy over your JSON into the JSON view

Then copy and paste the JSON below and click save:

{
"timeline": {
"soundtrack": {
"src": "https://shotstack-assets.s3.ap-southeast-2.amazonaws.com/music/unminus/ambisax.mp3",
"effect": "fadeOut"
},
"background": "#000000",
"tracks": [
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ streetAddress }}</p>",
"css": "p { color: #f0c20c; font-size: 40px; font-family: Roboto Black; text-align: left; }",
"width": 353,
"height": 141,
"position": "bottom"
},
"start": 1.2,
"length": 4.2,
"position": "center",
"offset": {
"x": -0.285,
"y": 0.19
},
"transition": {
"in": "slideRight",
"out": "slideLeft"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ suburb }}, {{ state }} {{ postcode }}</p>",
"css": "p { color: #ffffff; font-size: 22px; font-family: Roboto Black; text-align: left; }",
"width": 320,
"height": 66,
"position": "bottom"
},
"start": 1.3,
"length": 3.9,
"position": "center",
"offset": {
"x": -0.3,
"y": 0.034
},
"transition": {
"in": "slideRight",
"out": "slideLeft"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ propertyType }}</p>",
"css": "p { color: #f0c20c; font-size: 22px; font-family: Roboto Black; text-align: left; }",
"width": 320,
"height": 100,
"position": "top"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"x": -0.301,
"y": -0.246
},
"transition": {
"in": "slideRight",
"out": "slideLeft"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/bed.png"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"x": -0.445,
"y": -0.054
},
"transition": {
"in": "fade",
"out": "fade"
},
"scale": 1,
"fit": "none"
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/bath.png"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"x": -0.368,
"y": -0.054
},
"transition": {
"in": "fade",
"out": "fade"
},
"scale": 1,
"fit": "none"
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/car.png"
},
"start": 1.4,
"length": 4,
"offset": {
"x": -0.283,
"y": -0.055
},
"transition": {
"in": "fade",
"out": "fade"
},
"position": "center",
"fit": "crop",
"scale": 0.03
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ bedroomCount }}</p>",
"css": "p { color: #ffffff; font-size: 18px; font-family: Roboto Black; text-align: left; }",
"width": 36,
"height": 31,
"position": "center"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"y": -0.055,
"x": -0.405
},
"transition": {
"in": "fade",
"out": "fade"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ bathroomCount }}</p>",
"css": "p { color: #ffffff; font-size: 18px; font-family: Roboto Black; text-align: left; }",
"width": 36,
"height": 33,
"position": "center"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"x": -0.327,
"y": -0.055
},
"transition": {
"in": "fade",
"out": "fade"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">{{ garageSpace }}</p>",
"css": "p { color: #ffffff; font-size: 18px; font-family: Roboto Black; text-align: left; }",
"width": 60,
"height": 30,
"position": "center"
},
"start": 1.4,
"length": 4,
"position": "center",
"offset": {
"x": -0.227,
"y": -0.055
},
"transition": {
"in": "fade",
"out": "fade"
},
"fit": "none",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/images/real-estate-agent-male.jpg"
},
"start": 30,
"length": 6,
"scale": 0.4,
"offset": {
"x": 0,
"y": 0.232
},
"transition": {
"in": "fade"
},
"position": "center",
"fit": "none"
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">JEREMY SIMPSON</p>",
"css": "p { color: #f0c20c; font-size: 26px; font-family: Roboto Black; text-align: center; }",
"width": 600,
"height": 36,
"position": "center"
},
"start": 30,
"length": 6,
"offset": {
"x": 0,
"y": 0.02
},
"transition": {
"in": "fade"
},
"fit": "none",
"scale": 1,
"position": "center"
}
]
},
{
"clips": [
{
"asset": {
"type": "html",
"html": "<p data-html-type=\"text\">jeremy@blockrealestate.co</p>",
"css": "p { color: #ffffff; font-size: 18px; font-family: Roboto Black; text-align: center; }",
"width": 600,
"height": 64,
"position": "center"
},
"start": 30,
"length": 6,
"offset": {
"x": 0,
"y": -0.275
},
"transition": {
"in": "fade"
},
"fit": "none",
"scale": 1,
"position": "center"
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/logos/real-estate-white.png"
},
"start": 30,
"length": 6,
"scale": 0.26,
"offset": {
"x": 0,
"y": -0.131
},
"transition": {
"in": "fade"
},
"position": "center",
"fit": "none"
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/content-left.png"
},
"start": 0,
"offset": {
"x": -0.25,
"y": 0
},
"position": "center",
"transition": {
"in": "carouselRight",
"out": "carouselLeft"
},
"length": 5.6,
"scale": 1,
"fit": "contain"
},
{
"asset": {
"type": "image",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/outro-bottom.png"
},
"start": 29,
"offset": {
"x": 0,
"y": -0.249
},
"position": "center",
"length": 7,
"transition": {
"in": "carouselUp"
},
"fit": "crop",
"scale": 1
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "{{ image1 }}"
},
"start": 0,
"length": 6,
"effect": "zoomInSlow",
"transition": {
"in": "fade",
"out": "fade"
},
"offset": {
"x": 0,
"y": 0
},
"position": "center",
"fit": "crop",
"scale": 1
},
{
"asset": {
"type": "image",
"src": "{{ image3 }}"
},
"effect": "slideRightSlow",
"length": 7,
"transition": {
"in": "fade",
"out": "fade"
},
"offset": {
"x": 0,
"y": 0
},
"position": "center",
"start": 11
},
{
"asset": {
"type": "image",
"src": "{{ image5 }}"
},
"effect": "slideLeftSlow",
"start": 23,
"length": 7,
"transition": {
"in": "fade",
"out": "fade"
},
"offset": {
"x": 0,
"y": 0
},
"position": "center"
}
]
},
{
"clips": [
{
"asset": {
"type": "image",
"src": "{{ image2 }}"
},
"effect": "slideLeftSlow",
"start": 5,
"length": 7,
"offset": {
"x": 0,
"y": 0
},
"position": "center",
"transition": {
"in": "fade",
"out": "fade"
},
"fit": "crop",
"scale": 1
},
{
"asset": {
"type": "image",
"src": "{{ image4 }}"
},
"effect": "slideUpSlow",
"start": 17,
"length": 7,
"transition": {
"in": "fade",
"out": "fade"
}
},
{
"asset": {
"type": "image",
"src": "{{ image1 }}"
},
"effect": "zoomInSlow",
"start": 29,
"length": 7,
"transition": {
"in": "fade"
},
"offset": {
"x": 0,
"y": 0
},
"position": "center",
"fit": "crop",
"scale": 1,
"opacity": 0.6
}
]
}
]
},
"output": {
"format": "mp4",
"size": {
"width": 1024,
"height": 576
}
},
"merge": [
{
"find": "streetAddress",
"replace": "192 STOREY STREET"
},
{
"find": "suburb",
"replace": "MAROUBRA"
},
{
"find": "postcode",
"replace": "2003"
},
{
"find": "state",
"replace": "NSW"
},
{
"find": "bedroomCount",
"replace": "4"
},
{
"find": "bathroomCount",
"replace": "2"
},
{
"find": "garageSpace",
"replace": "1"
},
{
"find": "image1",
"replace": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/templates/real-estate-slideshow/realestate1.jpeg"
},
{
"find": "image2",
"replace": "https://shotstack-assets.s3.ap-southeast-2.amazonaws.com/images/realestate2.jpg"
},
{
"find": "image3",
"replace": "https://shotstack-assets.s3.ap-southeast-2.amazonaws.com/images/realestate3.jpg"
},
{
"find": "image4",
"replace": "https://shotstack-assets.s3.ap-southeast-2.amazonaws.com/images/realestate4.jpg"
},
{
"find": "image5",
"replace": "https://shotstack-assets.s3.ap-southeast-2.amazonaws.com/images/realestate5.jpg"
},
{
"find": "propertyType",
"replace": "HOUSE"
}
]
}

If you click on the JSON VIEW toggle again you will return to the preview view where you can preview the video:

Use the Shotstack Studio to edit your template

Make sure to save your template and take note of the template ID. The ID is the UUID in the URL. We will reference this ID when calling the render template endpoint.

Creating our Node.js application

Create a file named app.js and copy, paste and save the following script:

const axios = require('axios');

const NOTION_TOKEN = 'secret_JOJ0Li9J9KhsHjIA98HDVhoO1J9KHhGxZDAZxcGGjgY'; // Replace with your own Notion token
const NOTION_URL = 'https://api.notion.com';
const NOTION_DATABASE_TITLE = 'Real Estate' // The title of the Notion page containing the data
const SHOTSTACK_API_KEY = 'HBHjHylztJqjEeIFQxNJWB5g2mPa4lI40wX8oNVD'; // Replace with your own Shotstack API key
const SHOTSTACK_URL = 'https://api.shotstack.io/stage';
const TEMPLATE_ID = 'acad93a7-b8e9-4c21-a40a-2d062257547d'; // replace with your template ID

const renderVideo = async (streetAddress) => {
try {
const notionHeaders = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${NOTION_TOKEN}`,
'Notion-Version': '2022-06-28',
};

// Get the list of databases belonging to the API token
const notionDatabases = await axios.post(
`${NOTION_URL}/v1/search`,
{
filter: {
value: 'database',
property: 'object',
},
},
{
headers: notionHeaders
}
);

// Get the ID of the database that matches the database/page name
const notionDatabaseId = notionDatabases.data.results.find((database) => (
(database.title.find((item) => (item.plain_text === NOTION_DATABASE_TITLE)))
))?.id;

// Get the data from the Notion page/database table
const notionDataResponse = await axios.post(
`${NOTION_URL}/v1/databases/${notionDatabaseId}/query`,
{},
{
headers: notionHeaders,
}
);

// Prepare the Shotstack request
const notionData = notionDataResponse.data.results;
const notionMergeFields = await getMergeFieldDataFromNotion(notionData);
const mergeArray = getShotstackMergeFields(streetAddress, notionMergeFields);

const templateData = {
id: TEMPLATE_ID,
merge: [...mergeArray],
}

// Send the Shotstack template render request
const renderResponse = await axios.post(
`${SHOTSTACK_URL}/templates/render`,
JSON.stringify(templateData),
{
headers: {
'Content-Type': 'application/json',
'x-api-key': SHOTSTACK_API_KEY,
}
},
);

if (renderResponse.status !== 201) {
throw new Error(renderResponse);
}

console.log("\nRender successfully queued\n");

getStatus(renderResponse?.data?.response?.id)
} catch (error) {
console.log(error);
}
};

const getStatus = async (renderId) => {
try {
const statusResponse = await axios.get(
`${SHOTSTACK_URL}/render/${renderId}`,
{
headers: {
'Content-Type': 'application/json',
'x-api-key': SHOTSTACK_API_KEY,
}
},
);

if (statusResponse?.data?.response?.status !== 'done') {
console.log(`Status: ${statusResponse?.data?.response?.status}`);

return setTimeout(getStatus, 5000, renderId);
}

console.log(`\nRender complete: ${statusResponse.data?.response?.url}`);
} catch (error) {
console.log(error);
}
};

const getMergeFieldDataFromNotion = (notionResponse) => {
const mergedArray = notionResponse.map((item, index) => {
const data = {};

data.id = index + 1;
data.streetAddress = item?.properties['street address']?.title[0]?.plain_text;
data.bathroomCount = item?.properties['number of bathrooms']?.rich_text[0]?.plain_text;
data.bedroomCount = item?.properties['number of bedrooms']?.rich_text[0]?.plain_text;
data.propertyType = item?.properties['property type']?.rich_text[0]?.plain_text;
data.garageSpace = item?.properties['car spaces']?.rich_text[0]?.plain_text;
data.image1 = item?.properties['property image 1']?.url;
data.image2 = item?.properties['property image 2']?.url;
data.image3 = item?.properties['property image 3']?.url;
data.image4 = item?.properties['property image 4']?.url;
data.image5 = item?.properties['property image 5']?.url;
data.suburb = item?.properties.suburb?.rich_text[0]?.plain_text;
data.postcode = item?.properties.postcode?.rich_text[0]?.plain_text;
data.state = item?.properties.state?.rich_text[0]?.plain_text;

return data;
});

return mergedArray;
};

const getShotstackMergeFields = (streetAddress, notionMergeFields) => {
const arr = [];

const tempValue = notionMergeFields.find(
(element) => element?.streetAddress.toLowerCase() === streetAddress.toLowerCase(),
);

Object.keys(tempValue).forEach((elem) => {
arr.push({
find: elem,
replace: tempValue[elem],
});
});

const placeholderFields = [
'streetAddress',
'suburb',
'postcode',
'state',
'propertyType',
'bedroomCount',
'bathroomCount',
'garageSpace',
'image1',
'image2',
'image3',
'image4',
'image5',
'propertyType'
];

// filter out irrelevant fields
return (arr.filter((elem) => (
placeholderFields.includes(elem.find)
)));
};

renderVideo(process.argv[2]);

The script just needs some configuration and it is ready to run. But it is quite long so lets go through it in detail to understand what is going on.

Configuring the script

The first thing you will need to do is set up the configuration variables using your own keys and ID's:

const NOTION_TOKEN = 'secret_JOJ0Li9J9KhsHjIA98HDVhoO1J9KHhGxZDAZxcGGjgY'; // Replace with your own Notion token
const NOTION_URL = 'https://api.notion.com';
const NOTION_DATABASE_TITLE = 'Real Estate' // The title of the Notion page containing the data
const SHOTSTACK_API_KEY = 'HBHjHylztJqjEeIFQxNJWB5g2mPa4lI40wX8oNVD'; // Replace with your own Shotstack API key
const SHOTSTACK_URL = 'https://api.shotstack.io/stage';
const TEMPLATE_ID = 'acad93a7-b8e9-4c21-a40a-2d062257547d'; // replace with your template ID

Functions for managing Notion and Shotstack data

At the bottom of the script we create a couple of helper functions that will prepare the Notion data and Shotstack merge fields:

const getMergeFieldDataFromNotion = (notionResponse) => {
const mergedArray = notionResponse.map((item, index) => {
const data = {};

data.id = index + 1;
data.streetAddress = item?.properties['street address']?.title[0]?.plain_text;
data.bathroomCount = item?.properties['number of bathrooms']?.rich_text[0]?.plain_text;
data.bedroomCount = item?.properties['number of bedrooms']?.rich_text[0]?.plain_text;
data.propertyType = item?.properties['property type']?.rich_text[0]?.plain_text;
data.garageSpace = item?.properties['car spaces']?.rich_text[0]?.plain_text;
data.image1 = item?.properties['property image 1']?.url;
data.image2 = item?.properties['property image 2']?.url;
data.image3 = item?.properties['property image 3']?.url;
data.image4 = item?.properties['property image 4']?.url;
data.image5 = item?.properties['property image 5']?.url;
data.suburb = item?.properties.suburb?.rich_text[0]?.plain_text;
data.postcode = item?.properties.postcode?.rich_text[0]?.plain_text;
data.state = item?.properties.state?.rich_text[0]?.plain_text;

return data;
});

return mergedArray;
};

const getShotstackMergeFields = (streetAddress, notionMergeFields) => {
const arr = [];

const tempValue = notionMergeFields.find(
(element) => element?.streetAddress.toLowerCase() === streetAddress.toLowerCase(),
);

Object.keys(tempValue).forEach((elem) => {
arr.push({
find: elem,
replace: tempValue[elem],
});
});

const placeholderFields = [
'streetAddress',
'suburb',
'postcode',
'state',
'propertyType',
'bedroomCount',
'bathroomCount',
'garageSpace',
'image1',
'image2',
'image3',
'image4',
'image5',
'propertyType'
];

// filter out irrelevant fields
return (arr.filter((elem) => (
placeholderFields.includes(elem.find)
)));
};

In the code above, there is a function called getMergeFieldDataFromNotion. You will use the function to extract the required properties and values from the Notion database response object.

Also, there is another function called getShotstackMergeFields. This function will use the database value to return a merged array of values to replace the placeholders in our template.

Handling the render request

The main part of the application fetches property details from Notion by it's street address and sends the details to Shotstack for rendering:

const renderVideo = async (streetAddress) => {
try {
const notionHeaders = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${NOTION_TOKEN}`,
'Notion-Version': '2022-06-28',
};

// Get the list of databases belonging to the API token
const notionDatabases = await axios.post(
`${NOTION_URL}/v1/search`,
{
filter: {
value: 'database',
property: 'object',
},
},
{
headers: notionHeaders
}
);

// Get the ID of the database that matches the database/page name
const notionDatabaseId = notionDatabases.data.results.find((database) => (
(database.title.find((item) => (item.plain_text === NOTION_DATABASE_TITLE)))
))?.id;

// Get the data from the Notion page/database table
const notionDataResponse = await axios.post(
`${NOTION_URL}/v1/databases/${notionDatabaseId}/query`,
{},
{
headers: notionHeaders,
}
);

// Prepare the Shotstack request
const notionData = notionDataResponse.data.results;
const notionMergeFields = await getMergeFieldDataFromNotion(notionData);
const mergeArray = getShotstackMergeFields(streetAddress, notionMergeFields);

const templateData = {
id: TEMPLATE_ID,
merge: [...mergeArray],
}

// Send the Shotstack template render request
const renderResponse = await axios.post(
`${SHOTSTACK_URL}/templates/render`,
JSON.stringify(templateData),
{
headers: {
'Content-Type': 'application/json',
'x-api-key': SHOTSTACK_API_KEY,
}
},
);

if (renderResponse.status !== 201) {
throw new Error(renderResponse);
}

console.log("\nRender successfully queued\n");

getStatus(renderResponse?.data?.response?.id)
} catch (error) {
console.log(error);
}
};

Following the authentication procedure to call the Notion API we request a search of all your available databases. We then extract the database ID of the database that matches the NOTION_DATABASE_TITLE, which should be Real Estate.

If a database name match exists, we query the database query endpoint to fetch the records of the database and their values.

Once the merge array of values is created we are ready to process our video file. Shotstack provides us with a render template endpoint. After sending it a POST request with your template ID and the merge fields it will start rendering your video.

By calling the renderVideo function with a streetAddress parameter, we send our merge array to the render template endpoint, which returns a response with the ID of the video render request. We can use the ID to query the status of the video rendering task.

Querying the render status

When the renderVideo function completes it calls the getStatus function with the render id to check the video rendering status. Because rendering can take 30 to 40 seconds the function uses setTimeout to periodically check progress every 5 seconds.

After the video rendering completes our app returns the url of the completed video.

The code for the getStatus function is below:

const getStatus = async (renderId) => {
try {
const statusResponse = await axios.get(
`${SHOTSTACK_URL}/render/${renderId}`,
{
headers: {
'Content-Type': 'application/json',
'x-api-key': SHOTSTACK_API_KEY,
}
},
);

if (statusResponse?.data?.response?.status !== 'done') {
console.log(`Status: ${statusResponse?.data?.response?.status}`);

return setTimeout(getStatus, 5000, renderId);
}

console.log(`\nRender complete: ${statusResponse.data?.response?.url}`);
} catch (error) {
console.log(error);
}
};

Running our script

After following all the steps above, head to your terminal and type the command below to run your application:

node app.js '5121 Laguna Woods Drive'

This will run the script and pass the street address 5121 Laguna Woods Drive, one of the addresses in our Notion databases.

If the Notion database and address are found then you should see an output similar to below:

Render successfully queued

Status: queued
Status: rendering
Status: rendering
Status: rendering
Status: rendering
Status: rendering
Status: rendering

Render complete: https://shotstack-api-stage-output.s3-ap-southeast-2.amazonaws.com/49pzmo9f3e/78e2aee4-e0fe-4755-923e-bcac526b9705.mp4

The final output

Copy the URL returned by the script to your browser or download the file at the URL. The final result should be a real estate video generated for a specific address stored in Notion:

You can now run the script again but with a different address - any address that is available in the Notion database.

Conclusion

In this tutorial, your learned how to use the Shotstack and Notion APIs to generate real estate videos using a property's street address. The Shotstack API allows users to quickly and easily create custom videos using JSON data, while the Notion API allows users to store and access property information in a database.

In addition the APIs could be used to create videos for other types of businesses, such as car dealerships or retail stores. By using the APIs to create custom videos, businesses can quickly and easily create engaging content to promote their products or services.

Overall, the Shotstack and Notion APIs offer a powerful and versatile solution for creating custom videos and accessing data stored in a database. By using these APIs, businesses can save time and money while creating engaging and dynamic content to promote their products and services.

Get started with Shotstack's video editing API in two steps:

  1. Sign up for free to get your API key.
  2. Send an API request to create your video:
    curl --request POST 'https://api.shotstack.io/v1/render' \
    --header 'x-api-key: YOUR_API_KEY' \
    --data-raw '{
    "timeline": {
    "tracks": [
    {
    "clips": [
    {
    "asset": {
    "type": "video",
    "src": "https://shotstack-assets.s3.amazonaws.com/footage/beach-overhead.mp4"
    },
    "start": 0,
    "length": "auto"
    }
    ]
    }
    ]
    },
    "output": {
    "format": "mp4",
    "size": {
    "width": 1280,
    "height": 720
    }
    }
    }'
Femi Ige Muyiwa

BY FEMI IGE MUYIWA
6th December 2022

Studio Real Estate
Experience Shotstack for yourself.
SIGN UP FOR FREE

You might also like