Render from Studio Template
Render any template you've designed in Orshot Studio by making a POST request — as an image, PDF, or video, at any size, and optionally publish it.
Templates you design in Orshot Studio are Studio Templates — custom layouts whose text, images, and styles you parameterize and then render on demand from the API.
POST https://api.orshot.com/v1/studio/renderQuick start#
The smallest request: a template ID, your dynamic values, and the output you want back.
const res = await fetch("https://api.orshot.com/v1/studio/render", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer <ORSHOT_API_KEY>",
},
body: JSON.stringify({
templateId: <TEMPLATE_ID>,
modifications: {
title: "Custom Title",
imageUrl: "https://acme.com/cover.png",
},
response: { type: "url", format: "png" },
}),
});
const { data } = await res.json();
// data.content → the rendered image URLEverything else on this page is optional and layers on top of this: render at any size, as a PDF or video, or publish it to social.
Response#
data holds your render. Its shape depends on the template — expand the matching tab:
One page → data is an object. Read the render at data.content (a string).
{
"data": {
"content": "data:image/png;base64,iVBORw0.....", // URL or base64, per response.type
"format": "png",
"type": "base64",
"responseTime": 325.22
}
}The base64 type with a binary response isn't supported (a binary stream returns one file).
Add a publish object and the response also includes a publish array with
each account's status — see Publish to social.
Request body#
| Field | Type | Required | Description |
|---|---|---|---|
templateId | number | Yes | Your template's ID — shown on the template's page and in the playground. |
modifications | object | No | Your dynamic values, keyed by parameter ID. Multi-page templates prefix keys with the page, e.g. page2@title. You can also set style parameters to change layer styles at render time. |
response | object | No | What to render and how to return it (format, type, size, …). See below. |
pdfOptions | object | No | PDF-only output controls (margins, DPI, color mode, page range). See Render as a PDF. |
videoOptions | object | No | Video-only output controls (fps, trim, audio, subtitles, page transitions). See Render as a video. |
publish | object | No | Publish the render to connected social accounts. See Publish to social. |
The response object#
| Field | Type | Description |
|---|---|---|
format | string | Output format: png, webp, jpg, avif, pdf, mp4, webm, gif. Defaults to png. |
type | string | How content is returned: url, base64, or binary. |
scale | number | Output scale multiplier. 1 = template size, 2 = double, etc. |
size | string | Replace the render size — a preset slug (e.g. "instagram-story") or "WIDTHxHEIGHT" (e.g. "1200x630"). See Smart Resize. |
width + height | number | Explicit pixel dimensions instead of size (both required together, 10–5000px). |
extraSizes | array | object | Add the same design at extra sizes in one call. See Render multiple sizes. |
includePages | number[] | Multi-page only — render just these pages, e.g. [1, 3]. |
fileName | string | Custom output file name (no extension). url/binary types only. Carousel files are suffixed -page-1, -page-2, …; url outputs also get a -[HASH] suffix to stay unique. |
Smart Resize#
Render a template at a different canvas size without redesigning it — design once, then generate platform-ready outputs for every channel from the same template and modifications. Want to set this up visually first? See Smart Resize in Studio.
The layout adapts deterministically (no AI) — the same template and size always produce the same output:
- Everything scales uniformly, so proportions and text wrapping are preserved.
- Backgrounds and full-bleed images stretch to cover the new canvas.
- Elements re-anchor to the edge/center they were designed against (a top-left logo stays top-left; a footer stays pinned to the bottom).
- Grouped elements (e.g. a heading + subheading) move together as one unit.
- A safety guard guarantees the adapted layout never introduces overlapping elements.
Render at a different size#
size replaces the render size; the response shape is unchanged.
response: {
type: "url",
format: "png",
size: "instagram-story", // preset slug, or "1080x1920", or use width + height
}Available presets — or pass any "WIDTHxHEIGHT" string, or width + height (10–5000px):
instagram-story · instagram-post · instagram-post-portrait · instagram-post-landscape · facebook-post · facebook-story · facebook-cover · twitter-post · twitter-header · linkedin-post · linkedin-banner · youtube-thumbnail · youtube-short · tiktok-video · pinterest-pin · whatsapp-status · og-image · blog-header · email-header · website-banner · presentation-16-9 · a4-document · us-letter · business-card · zoom-background · leaderboard-ad · medium-rectangle-ad
response.scalestill applies on top of the adapted size —size: "1080x1920"withscale: 2outputs 2160×3840.- Best with moderate aspect-ratio changes; extreme changes (e.g. square →
leaderboard-ad) stay safe but elements may look small relative to the canvas.
Render multiple sizes in one call#
Where size replaces the output, extraSizes is purely additive: your normal response is unchanged, and each output gains an extraSizes array nested right beside it.
Pass an array of sizes:
response: { type: "url", format: "png", extraSizes: ["1080x1920", "1080x1080", "1200x630"] }…or a named object, when you want a label to route on:
response: { type: "url", format: "png", extraSizes: { story: "1080x1920", square: "1080x1080" } }The extra sizes nest beside your render — single-page on the data object, multi-page on each page (so a size is always self-describing about which page it belongs to):
{
"data": {
"content": "https://…", // your normal render (native or `size`)
"extraSizes": [
{ "size": "1080x1920", "width": 1080, "height": 1920, "content": "https://…" },
{ "size": "1080x1080", "width": 1080, "height": 1080, "content": "https://…" }
]
}
}A named size adds a name field to each entry, e.g. { "name": "story", "size": "story", … }.
Combine with size — size sets the base render, extraSizes add extra copies beside it:
// base rendered at 1200x630, plus a 1080x1920 copy
response: { type: "url", format: "png", size: "1200x630", extraSizes: ["1080x1920"] }Leave size off and data stays your original render — so extraSizes: ["1080x1920"] gives you the original and the story size.
Supported & billed. Image formats only (png, jpg, webp, avif)
with response.type url or base64 — PDF and video are coming, and
extraSizes isn't supported on templates with flowing (auto-expanding)
elements. Up to 50 extra outputs per call (pages × sizes). Each extra
output is billed like a page (2 renders = 1 credit). A size that fails
comes back with an error field instead of content — it doesn't break the
rest of the response and isn't billed.
Reproduce studio-approved sizes#
For pixel-perfect output, open your template in the studio and add sizes from the Smart Resize panel — each gets an instant adaptive preview, can be recomposed with AI, and is saved with the template. A render that requests a saved size reproduces that approved preview exactly, every time; the deterministic adaptation above is only used for sizes you haven't saved.
- Works with every response format and with multi-page templates (every page is adapted).
- Saved sizes apply whether the request comes from your own backend or an embed — your customers resize in the embed, you render the saved sizes via the API.
Render as a PDF#
Set response.format to pdf and add an optional pdfOptions object for print controls (margins, DPI, color mode, page range).
body: JSON.stringify({
templateId: <TEMPLATE_ID>,
modifications: { name: "Sarah Chen", course: "UX Design" },
response: { type: "url", format: "pdf" },
pdfOptions: { margin: "0px", dpi: 300, colorMode: "cmyk" },
}),See PDF Options for every field (margins incl. per-side overrides, DPI, RGB/CMYK, page ranges) and print recommendations.
Render as a video#
For templates with video elements, set response.format to mp4, webm, or gif.
body: JSON.stringify({
templateId: <TEMPLATE_ID>,
modifications: {
promoVideo: "https://example.com/clip.mp4", // a video element's parameter ID
"promoVideo.trimStart": 0,
"promoVideo.trimEnd": 10,
},
response: { type: "url", format: "mp4" }, // or "webm", "gif"
videoOptions: { fps: 30, muted: true, combinePages: true, pageTransition: "fade" },
}),- Per-element controls (custom URL,
.trimStart,.trimEnd,.muted,.loop) → see Video Elements. - Render-level
videoOptions(fps, quality, trim, audio overrides, subtitles + styling, multi-page combine & transitions) → see Video Options. - Page-level audio tracks and subtitles set in Studio are included automatically; see Audio & Subtitles.
For multi-page video, set videoOptions.combinePages: true to get one
combined video instead of one file per page (supported for mp4/webm; GIF
always returns per-page files).
Publish to social#
Add a publish object to post the render straight to connected social accounts — no extra calls. The response gains a publish array with each account's status.
body: JSON.stringify({
templateId: <TEMPLATE_ID>,
modifications: { title: "New post is live!" },
response: { type: "url", format: "png" },
publish: {
accounts: [1, 2], // social account IDs from your workspace
content: "Check out our latest design!",
},
}),See Publish from API for the full object — drafts (isDraft), scheduling (schedule.scheduledFor, timezone), per-account platformOptions, statuses, and error handling.
Rate limits#
This endpoint is rate limited based on your plan. Check the response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute for your plan |
X-RateLimit-Remaining | Remaining requests in the current window |
Retry-After | Seconds to wait (only on 429 responses) |
Ready to automate?
Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.
Get your API key- Image, PDF and video generation via API
- Visual editor with AI and smart layouts
- Zapier, Make, MCP and 50+ integrations
- White-label embed for your own app
- 30 free credits — no credit card required