Start an Async Render

Add response.mode "async" to any render to run it in the background, then collect the result by polling or webhook. Built for video, big PDFs, and batch pipelines that outlive HTTP timeouts.

Updated


A synchronous render holds the HTTP connection for the whole render. That is fine for images, but video renders can take minutes, longer than most client timeouts (Zapier ~30s, Make ~60s, many AI agents 120s). Async mode returns immediately with a job, the render runs server-side, and you collect the result by polling or via a webhook.

POST https://api.orshot.com/v1/studio/render   (with response.mode: "async")

The related endpoints for working with the job it creates:

Start an async render#

Add mode: "async" to the response object of a normal render request. Everything else (modifications, videoOptions, Smart Resize, publish) works exactly the same. Async works for any output format: video, PDF, or image.

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: "Launch day!" },
    response: {
      mode: "async", // the one field that makes this a background job
      type: "url",
      format: "mp4", // or "pdf", "png", …
    },
    videoOptions: { duration: 30, fps: 30 },
  }),
});

const job = await res.json(); // HTTP 202
{
  "id": 1204,
  "status": "queued",
  "finished": false,
  "created_at": "2026-08-25T10:30:00.000Z",
  "self": "https://api.orshot.com/v1/studio/render-jobs/1204"
}

Async mode requires response.type: "url" (the default). Results are delivered as hosted URLs, not base64 or binary.

The job object#

Every async endpoint returns this shape. Poll Get a Render Job until finished is true.

FieldTypeDescription
idnumberThe job's id. Use it to poll, list, or cancel.
statusstringqueued, processing, succeeded, failed, or canceled.
finishedbooleanfalse while the job is queued or processing, true once it reaches a terminal status. Poll until this is true rather than comparing statuses, so new statuses can't break your integration.
resultobjectOnly on succeeded. The exact same response body a synchronous render returns (data, format, publish, …), so your handling code is mode-agnostic.
errorstringOnly on failed. A human-readable, actionable message.
error_codestringOnly on failed. A stable machine code, e.g. video-duration-exceeds-plan, invalid-video-duration, render-timeout.
metadatastringYour metadata string, echoed back verbatim (max 1024 chars).
created_at / started_at / completed_atstringISO timestamps for each phase.
expires_atstringISO timestamp when the job record is removed (7 days after creation). The rendered asset is unaffected.
selfstringThe job's poll URL.

Webhook (optional)#

Pass a webhook_url in the render request and Orshot POSTs the finished job to it, no polling needed.

body: JSON.stringify({
  templateId: <TEMPLATE_ID>,
  response: { mode: "async", format: "mp4" },
  webhook_url: "https://acme.com/hooks/orshot",
  metadata: "order-8412", // echoed back so you can correlate
}),

The delivery payload:

{
  "event": "render_job.finished",
  "job": { "id": 1204, "status": "succeeded", "finished": true, "result": { ... }, "metadata": "order-8412" }
}
  • Delivery is attempted 3 times (immediately, +30s, +2m) with a 10-second timeout each; respond with any 2xx to acknowledge.
  • The URL must be publicly reachable over http(s). Private and loopback addresses are rejected at create time.
  • Webhooks are a convenience; polling remains the source of truth. If delivery fails, the job is still there to poll.
  • Jobs you cancel do not fire a webhook (you already know the outcome). Every other terminal status does.

Statuses & guarantees#

StatusMeaning
queuedAccepted; the render has not started yet.
processingRendering now.
succeededDone. result holds the render response.
failedDid not complete. error says why and what to change; error_code is stable.
canceledYou canceled it before it started. Nothing was billed.
  • Renders have a 15-minute ceiling. A job that exceeds it fails with error_code: "render-timeout" (reduce duration/fps/quality or split pages into separate renders). A render that keeps running and completes just after the ceiling may still be billed, even though the job reads failed.
  • Credits are charged exactly as in sync mode, on render completion. A job that never renders (validation failure, or canceled while queued) bills nothing; a render that actually ran is billed even if the job later reads failed.
  • Jobs are scoped to your workspace; a job id from another workspace answers 404.

When to use async vs sync#

Use sync (default)Use async
Images and PDFs (typically < 5s)Any video render
You want the file in one requestYour platform enforces short HTTP timeouts (Zapier, Make, AI agents/MCP)
Simplest possible integrationLong or multi-page renders, or fire-many-collect-later pipelines

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