Quickstart
Generate your first personalized video in 5 minutes. This guide walks through creating a render, polling for completion, and downloading the video.
Prerequisites
- • A Significant Figures account (Studio tier or above for API access)
- • An API key — create one in your dashboard
- • curl, Python 3, or Node.js installed
1Get Your API Key
Create an API key in your dashboard settings. Choose the render-batch preset for full render access.
sf_live_a1b2c3d4e5f6789... # Copy and save this — it won't be shown again!
Security: Never commit API keys to version control. Use environment variables or secret managers.
2Make Your First Render Request
Let's create a video using the Emeritus template (academic research portrait). This template auto-fetches data from OpenAlex — you just need an author ID.
Using curl
curl -X POST https://app.sigfigsstudio.com/api/render \
-H "Content-Type: application/json" \
-H "X-API-Key: sf_live_YOUR_KEY_HERE" \
-d '{
"templateId": "emeritus",
"data": {
"authorId": "A5001825849"
},
"brand": {
"accentColor": "#14b8a6"
},
"audio": {
"includeVoiceover": true,
"voiceId": "en-GB-RyanNeural"
}
}'Using Python
import requests
import os
API_KEY = os.environ["SIGFIGS_API_KEY"]
BASE_URL = "https://app.sigfigsstudio.com/api"
# Create render job
response = requests.post(
f"{BASE_URL}/render",
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
json={
"templateId": "emeritus",
"data": {
"authorId": "A5001825849"
},
"brand": {
"accentColor": "#14b8a6"
},
"audio": {
"includeVoiceover": True,
"voiceId": "en-GB-RyanNeural"
}
}
)
result = response.json()
print(f"Job created: {result['jobId']}")
print(f"Credits remaining: {result['creditsRemaining']}")Using Node.js
const API_KEY = process.env.SIGFIGS_API_KEY;
const BASE_URL = "https://app.sigfigsstudio.com/api";
const response = await fetch(`${BASE_URL}/render`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify({
templateId: "emeritus",
data: {
authorId: "A5001825849"
},
brand: {
accentColor: "#14b8a6"
},
audio: {
includeVoiceover: true,
voiceId: "en-GB-RyanNeural"
}
}),
});
const result = await response.json();
console.log(`Job created: ${result.jobId}`);
console.log(`Credits remaining: ${result.creditsRemaining}`);Response
{
"success": true,
"jobId": "78008b5b-4207-4b2e-a1c5-9b923f519d4c",
"status": "pending",
"templateId": "emeritus",
"displayName": "Dr. Jane Smith",
"creditsRemaining": 97,
"message": "Render job created. Poll GET /api/render/[jobId] for status."
}Save the jobId — you'll use it to check render status.
3Poll for Completion
Videos typically take 30-120 seconds to render. Poll GET /api/render/:id until status is "complete".
Using curl
curl -H "X-API-Key: sf_live_YOUR_KEY_HERE" \ https://app.sigfigsstudio.com/api/render/78008b5b-4207-4b2e-a1c5-9b923f519d4c
Using Python (with polling loop)
import requests
import time
import os
API_KEY = os.environ["SIGFIGS_API_KEY"]
BASE_URL = "https://app.sigfigsstudio.com/api"
JOB_ID = "78008b5b-4207-4b2e-a1c5-9b923f519d4c"
while True:
response = requests.get(
f"{BASE_URL}/render/{JOB_ID}",
headers={"X-API-Key": API_KEY}
)
data = response.json()
status = data["job"]["status"]
print(f"Status: {status}")
if status == "complete":
print(f"✓ Video ready: {data['job']['outputUrl']}")
break
elif status == "failed":
print(f"✗ Render failed: {data['job'].get('error')}")
break
time.sleep(5) # Poll every 5 secondsUsing Node.js (with polling loop)
const API_KEY = process.env.SIGFIGS_API_KEY;
const BASE_URL = "https://app.sigfigsstudio.com/api";
const JOB_ID = "78008b5b-4207-4b2e-a1c5-9b923f519d4c";
const poll = setInterval(async () => {
const response = await fetch(`${BASE_URL}/render/${JOB_ID}`, {
headers: { "X-API-Key": API_KEY }
});
const data = await response.json();
const status = data.job.status;
console.log(`Status: ${status}`);
if (status === "complete") {
clearInterval(poll);
console.log(`✓ Video ready: ${data.job.outputUrl}`);
} else if (status === "failed") {
clearInterval(poll);
console.log(`✗ Render failed: ${data.job.error}`);
}
}, 5000); // Poll every 5 secondsResponse (complete)
{
"success": true,
"job": {
"id": "78008b5b-4207-4b2e-a1c5-9b923f519d4c",
"status": "complete",
"outputUrl": "https://skkroo2x4wm2v7c8.public.blob.vercel-storage.com/emeritus/78008b5b.mp4",
"authorName": "Dr. Jane Smith",
"createdAt": "2026-03-19T19:18:53.849Z",
"completedAt": "2026-03-19T19:19:20.614Z"
}
}4Download Your Video
The outputUrl is publicly accessible. Download it with curl, wget, or any HTTP client.
curl -O https://skkroo2x4wm2v7c8.public.blob.vercel-storage.com/emeritus/78008b5b.mp4 # Or using wget wget https://skkroo2x4wm2v7c8.public.blob.vercel-storage.com/emeritus/78008b5b.mp4
import requests
video_url = "https://skkroo2x4wm2v7c8.public.blob.vercel-storage.com/emeritus/78008b5b.mp4"
response = requests.get(video_url)
with open("output.mp4", "wb") as f:
f.write(response.content)
print("✓ Video downloaded: output.mp4")Video URLs are permanent — they don't expire. You can embed them directly or download and re-host.
Next Steps
Batch Rendering
Generate 1,000 personalized videos from a CSV in one API call. Perfect for campaign launches.
Learn More →Explore Templates
Browse all 5 templates. Each has different data requirements and auto-fetch capabilities.
View Templates →Set Up Webhooks
Get notified when renders complete instead of polling. Receive events via HTTP POST.
Configure Webhooks →API Key Scopes
Understand granular permissions and create keys with minimal required access.
Learn About Scopes →Tips & Best Practices
Use Environment Variables for API Keys
Never hardcode keys in your source code. Use process.env or os.environ.
Poll Responsibly
Poll every 5-10 seconds, not every second. Rate limit is 120 req/min for GET /render/:id.
Handle Errors Gracefully
Check for status === "failed" in polling loops. The error field contains details.
Monitor Credit Usage
The render response includes creditsRemaining. Track this to avoid running out mid-campaign.