Templates & Personalization
How to render the same composition with different text, images, video, and audio per request using the isDynamic + replacements system.
A "template" in Renderly is a composition where some overlays are marked as variables. At render time you pass a replacements object and the marked overlays swap their content. One template = thousands of personalized outputs.
The two pieces
Personalization rests on two coordinated fields on an overlay:
isDynamic: true— flags the overlay as a variable. Without this, the overlay is static and replacements don't touch it.name: "<key>"— the lookup key that links this overlay to a value inreplacements.
At render time the server walks every overlay; if isDynamic is true, it looks up name in replacements and swaps the appropriate field:
| Overlay type | Field replaced |
|---|---|
text, shape | content |
video, image, sound | src |
There is no {{ variable }} syntax inside content strings. The unit of replacement is a whole overlay, identified by its name.
A complete template
{
"replacements": {
"propertyAddress": "123 Main St",
"agentPhoto": "https://example.com/agent-default.jpg",
"backgroundVideo": "https://example.com/bg-default.mp4"
},
"overlays": [
{
"id": 0, "type": "video",
"name": "backgroundVideo", "isDynamic": true,
"src": "https://example.com/bg-default.mp4",
"from": 0, "durationInFrames": 180,
"row": 0, "top": 0, "left": 0, "width": 1080, "height": 1920,
"styles": { "objectFit": "cover" }
},
{
"id": 1, "type": "text",
"name": "propertyAddress", "isDynamic": true,
"content": "123 Main St",
"from": 10, "durationInFrames": 170,
"row": 1, "top": 200, "left": 60, "width": 960, "height": 200,
"styles": { "fontSize": "4rem", "color": "#FFF", "textAlign": "center" }
},
{
"id": 2, "type": "image",
"name": "agentPhoto", "isDynamic": true,
"src": "https://example.com/agent-default.jpg",
"from": 30, "durationInFrames": 150,
"row": 2, "top": 1500, "left": 390, "width": 300, "height": 300,
"styles": { "borderRadius": "50%", "objectFit": "cover" }
},
{
"id": 3, "type": "text",
"content": "Listed by Renderly Realty",
"from": 0, "durationInFrames": 180,
"row": 3, "top": 1850, "left": 60, "width": 960, "height": 50,
"styles": { "fontSize": "1rem", "color": "#FFF", "textAlign": "center" }
}
],
"durationInFrames": 180, "fps": 30,
"width": 1080, "height": 1920
}In this example, overlays 0–2 are variables; overlay 3 is a static watermark that every rendered video keeps unchanged.
Two important things about the replacements block:
- It lives at the top level of the composition (next to
overlays), not inside any overlay. - The values you put in it are defaults — they're used when the caller doesn't pass an override. This is what makes templates self-rendering for preview purposes.
Rendering a template
You can store a template centrally and call it by ID, or you can pass the whole composition inline and override its variables.
Template stored by Renderly
If the composition lives in our public template gallery, send templateId:
curl -X POST https://renderly.video/api/v1/renders \
-H "Authorization: Bearer rnd_..." \
-H "Content-Type: application/json" \
-d '{
"templateId": "tpl_real_estate_v1",
"replacements": {
"propertyAddress": "456 Ocean Drive",
"agentPhoto": "https://your-cdn.example.com/agents/jane.jpg",
"backgroundVideo": "https://your-cdn.example.com/listings/456-ocean.mp4"
}
}'The server loads tpl_real_estate_v1's composition, applies your replacements, and queues the render.
Your own project
If the composition is a project you built in the editor, send projectId instead. Same payload shape — replacements still match by overlay name:
curl -X POST https://renderly.video/api/v1/renders \
-H "Authorization: Bearer rnd_..." \
-H "Content-Type: application/json" \
-d '{
"projectId": "proj_my_listing_template",
"replacements": {
"propertyAddress": "456 Ocean Drive",
"agentPhoto": "https://your-cdn.example.com/agents/jane.jpg"
}
}'See Rendering — three modes for the full picture.
Resolution / duration overrides
Both templateId and projectId calls accept top-level overrides:
{
"templateId": "tpl_real_estate_v1",
"replacements": { /* ... */ },
"width": 1080,
"height": 1080,
"fps": 30,
"durationInFrames": 300
}Use this to render a 9:16 template at 1:1 for Instagram without forking the template, or to crop its length down.
Discovering a template's variables
Three ways:
- One public template, full contract —
GET /templates/{templateId}/variablesreturns each variable'sname(the exact key to send),type(textorurl), thefieldyour value is written to, the authoreddefault, and — for fixed-size text —maxCharactersandlinesAvailable. Prefer this: it is the only endpoint that tells you how much text fits, and a value longer thanmaxCharactershas its tail clipped rather than shrunk.GET /projects/{projectId}/variablesreturns the identical shape for your own projects. - Every public template at once —
GET /templatesreturns each template with avariables[]array carryingname,defaultValue, andtype. Good for browsing; it omits the capacity fields. - Your own project, visually — open it in the editor and look at the Variables panel, which lists every overlay flagged
isDynamic.
Each public template's page on renderly.video also lists its variable keys and character limits, with a ready-to-copy request for both POST /previews and POST /renders.
Flat-key shortcut for no-code platforms
Tools like Make, Zapier, or Pipedream often can't easily nest a replacements object inside JSON. To support them, Renderly treats any unknown top-level key as a replacement:
{
"templateId": "tpl_real_estate_v1",
"propertyAddress": "456 Ocean Drive",
"agentPhoto": "https://your-cdn.example.com/agents/jane.jpg"
}Internally these get merged into replacements. If a key appears both flat and inside replacements, the explicit replacements value wins.
Patterns for keeping templates clean
- Pick stable names —
headline,backgroundVideo,ctaText. These are user-facing keys. - Always include defaults — store sensible default values in the
replacementsobject so the template renders standalone (great for previews). - Lock down static elements — leave
isDynamicunset on watermarks, logos, and branded chrome you don't want callers overriding. - Group related variables — when one logical thing needs two fields (e.g. an avatar URL and a name caption), use related names like
agentPhoto/agentNameso consumers can spot the pairing.
Where to next
- Overlays reference — every overlay type and its fields
- Rendering — modes, polling, credit cost
- API reference: POST /renders — the full endpoint spec