Shotstack Template Render API
Shotstack Template Render — render a video from a saved template by replacing merge field placeholders. Async render → poll until done → OSS-hosted MP4. Billed per second of rendered video duration.
Quickstart
Video generation uses /v1/run. The quickstart below shows the synchronous form, but video routinely takes 60–180s and exceeds most client / proxy / serverless timeouts — use opt-in async mode (Async mode section below) in production. Response includes a signed URL to the generated MP4.
bash# VEO generation is synchronous (60–180s per clip). Set timeout >= 300s.
curl --max-time 300 https://api.skillboss.co/v1/run \
-H "Authorization: Bearer $SKILLBOSS_WHOLESALE_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "shotstack/template-render",
"inputs": {
"prompt": "A cat walking on the beach at sunset",
"duration_seconds": 8,
"aspect_ratio": "16:9",
"resolution": "1080p"
}
}'Your first 200 response is the fastest way to confirm setup. From there, swap in your real prompt and tune the model-specific parameters listed below.
Authentication
Every request must include your wholesale key. The header name depends on the endpoint — match the SDK you're using:
bashAuthorization: Bearer $SKILLBOSS_WHOLESALE_KEYTreat the wholesale key like a password — never commit it to source control or ship it in client-side bundles. Rotate from the wholesale dashboard if exposed. Standard (non-wholesale) console keys are rejected at the gateway with 401.
Code examples
pythonimport os, requests
# VEO synchronous generation typically takes 60–180s. Set timeout ≥ 300s.
resp = requests.post(
"https://api.skillboss.co/v1/run",
headers={"Authorization": f"Bearer {os.environ['SKILLBOSS_WHOLESALE_KEY']}"},
json={
"model": "shotstack/template-render",
"inputs": {
"prompt": "A cat walking on the beach at sunset",
"duration_seconds": 8,
"aspect_ratio": "16:9",
"resolution": "1080p",
},
},
timeout=300,
)
data = resp.json()
print(data["outputs"][0]["url"]) # signed URL to the generated videotypescript// VEO synchronous generation typically takes 60–180s.
// AbortController with a 300s timeout handles the long wait safely.
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 300_000);
const resp = await fetch("https://api.skillboss.co/v1/run", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SKILLBOSS_WHOLESALE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "shotstack/template-render",
inputs: {
prompt: "A cat walking on the beach at sunset",
duration_seconds: 8,
aspect_ratio: "16:9",
resolution: "1080p",
},
}),
signal: controller.signal,
});
clearTimeout(timer);
const data = await resp.json();
console.log(data.outputs[0].url); // signed URL to the generated videoParameters
Pass the model identifier as a top-level model field, and all generation parameters under an inputs object. The exact set of inputs.* fields depends on the model — see the model card on the wholesale dashboard for advanced options.
Async mode (recommended for production)
Image and video generation can take from tens of seconds to several minutes. The default /v1/run call is synchronous — it holds the HTTP connection until the result is ready, which routinely exceeds client / proxy / serverless function timeouts and pins a worker for the full duration.
Opt into async mode by adding "async": true to the request body (or by supplying webhook_url). Submit returns in <1s with a request_id; you then poll the status URL or receive the result via webhook. This is the standard queue-based pattern used by every async-generation provider — clients designed for that shape work without changes.
bashcurl -X POST https://api.skillboss.co/v1/run \
-H "Authorization: Bearer $SKILLBOSS_WHOLESALE_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "shotstack/template-render",
"inputs": { "prompt": "your prompt here" },
"async": true
}'Response:
json{
"request_id": "c546520207fb4b68b0adce825f9dcf6d",
"status": "IN_QUEUE",
"status_url": "https://api.skillboss.co/v1/run/status/c546520207fb4b68b0adce825f9dcf6d",
"cancel_url": "https://api.skillboss.co/v1/run/cancel/c546520207fb4b68b0adce825f9dcf6d"
}The same request_id is also returned in the x-request-id response header. A separate x-record-id header carries the internal deduct/audit row id — quote it when reporting an issue so support can find the call in our logs. Day-to-day client code only needs request_id.
bashcurl https://api.skillboss.co/v1/run/status/{request_id} \
-H "Authorization: Bearer $SKILLBOSS_WHOLESALE_KEY"Status enum — the full lifecycle:
Polling loop: hit the status endpoint every 5–10s; stop the moment you see COMPLETED / FAILED / CANCELLED — those are terminal. Result payload (when COMPLETED) is the same shape as the synchronous response — fetched via the result field. We retain completed results for 7 days, then they expire.
bashcurl -X POST https://api.skillboss.co/v1/run/cancel/{request_id} \
-H "Authorization: Bearer $SKILLBOSS_WHOLESALE_KEY"Returns 200 with cancelled: true on success, 200 idempotent no-op if already terminal, 409if the worker has already started the vendor call (the cost is already being incurred upstream and we don't support mid-flight abort), or 404 if the request_id is unknown or not owned by your account.
Add "webhook_url": "https://your-server.com/cb" to the submit body. We POST the result to that URL once the job reaches a terminal state. Payload format:
json// On success (COMPLETED → status "OK")
{
"request_id": "c546...",
"status": "OK",
"payload": { "generated_images": [{ "url": "https://..." }] },
"error": null
}
// On failure (FAILED → status "ERROR")
{
"request_id": "c546...",
"status": "ERROR",
"payload": null,
"error": "<error description>"
}status fields — by design. The status endpoint returns the 5-value lifecycle enum (IN_QUEUE / IN_PROGRESS / COMPLETED / FAILED / CANCELLED). The webhook body uses the 2-value 2-value success enum (OK / ERROR) because webhooks fire only on terminal states and a binary outcome is all the receiver needs. Webhooks are not sent on CANCELLED (you initiated it, no notification needed).Webhook security: the URL must be HTTPS and resolve to a public IP (loopback / RFC1918 / link-local / metadata addresses are rejected at submit time as an SSRF guard). Delivery is best-effort with one retry; have your endpoint be idempotent on request_id.
COMPLETED — FAILED and CANCELLED jobs incur no charge.Endpoint
| Method | POST |
| URL | https://api.skillboss.co/v1/run |
| Auth header | Authorization: Bearer $SKILLBOSS_WHOLESALE_KEY |
| Content-Type | application/json |
| Streaming | No SSE streaming, but supports async submit (set `"async": true`) which is the recommended path for long jobs — see the Async mode section. |
Errors
The API uses standard HTTP status codes:
Pricing
Wholesale pricing is your account-specific discount × vendor list price. Discount rate depends on your contract — see the live numbers on the wholesale dashboard. The dashboard shows your current cost per 1M tokens (or per image / per second) for every model.
No platform markup on standard token billing. Volume tiers + monthly caps are configurable per key.