Skip to main content

Bulk Rendering

Bulk rendering submits one template + many variable sets in a single request — Zvid fans it out into individual render jobs, each with its own credits, status, and output. It's the scale half of templates: personalized videos from a CRM export, one video per product, per city, per employee.

Submitting a batch​

curl -X POST https://api.zvid.io/api/render/bulk/api-key \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "tpl_xxxxxxxxxxxxxxxxxxxx",
"name": "spring-campaign",
"items": [
{ "variables": { "firstName": "Amira", "city": "Cairo" }, "name": "spring-amira" },
{ "variables": { "firstName": "Omar", "city": "Alexandria" } },
{ "variables": { "firstName": "Lina", "city": "Giza" } }
],
"webhookUrl": "https://example.com/hooks/bulk-done"
}'

Envelope fields:

FieldRequiredNotes
template or payloadone of themA stored tpl_… id, or a full inline project (with variables defaults).
itemsyes1–500 entries; each is { variables, name? }. Your plan's maxBulkItems may be lower — validation errors report it.
variablesnoBatch-level values merged under every item's variables.
overridesnoOutput knobs applied to every job (name, dimensions, format, …).
namenoBatch name shown in the dashboard.
webhookUrlnoUnsigned per-request callback for each child's completion or failure; not one batch event.

Best-effort validation​

Items are validated individually. Valid items become jobs immediately; invalid ones are returned in errors — each entry carries the item's original request index as item (0-based) plus field-level details — and the batch is only rejected outright when the envelope is malformed or every item fails:

{
"bulkId": "blk_…",
"queued": true,
"totalJobs": 2,
"rejectedItems": 1,
"errors": [
{
"item": 1,
"error": "Validation failed",
"details": [
{
"field": "payload.duration",
"message": "Duration must be at least 0.1 seconds"
}
]
}
],
"failedToQueue": 0,
"creditsReserved": 12,
"clientRoom": "api:42:7",
"queueAhead": 0,
"jobs": [
{
"jobId": "550e8400-…",
"index": 0,
"name": "spring-amira",
"creditsReserved": 6
},
{ "jobId": "6ba7b810-…", "index": 2, "name": null, "creditsReserved": 6 }
]
}

jobs[].index is each queued job's original position in items, so you can line results back up with your source rows even when some items were rejected.

Fix the failed rows and resubmit just those — the dashboard's bulk page has a fix-and-resubmit flow and CSV export for exactly this.

Credits​

Video jobs reserve credits individually before they are queued, then refund failed work. Image batches reserve one credit per accepted image at the batch level and reconcile failed child jobs when the batch finishes. See Credits, Plans & Limits. Neither validation nor template preview reserves credits.

Before submitting a batch, validate representative datasets with POST /api/render/validate/api-key. This checks one resolved request at a time; it is not a bulk dry-run endpoint. Split larger imports into batches within your account's maxBulkItems and the 500-item hard limit.

Tracking a batch​

EndpointAuthPurpose
GET /api/render/bulkJWT or API keyList your bulk batches
GET /api/render/bulk/{id}JWT or API keyBatch status with per-item job states and results

Individual jobs are also visible through the normal GET /api/jobs/{id} endpoint, and each fires webhook events on completion.

Keep bulkId and every returned jobs[].jobId. Separate rejected items from accepted jobs that later fail. A timeout after submission does not establish that nothing was queued: inspect the saved batch/job identifiers before retrying. See Errors and retries.

Bulk images​

POST /api/render/image/bulk (and /image/bulk/api-key) is the same contract with type: "image" enforced — useful for thumbnail sets and social-card batches. Images cost 1 credit per image: 25 images cost 25 credits. Credits are reserved once per batch, with 1 credit refunded for each failed image after the batch finishes.