Liveness probe. Returns 200 OK when the API process is up. No authentication required.
Response
{ "status": "ok" }Generate videos from a single image and an audio file via a simple HTTP API. Submit a job, poll for status, get back a finished video.
Every request to the API (except health checks) must include your API key in the X-API-Key request header. Keep it secret — anyone with the key can submit jobs on your behalf.
Submit your first job with a single curl command. The response includes the jobId you'll use to poll for status.
curl -X POST https://example.com/api/jobs \ -H "X-API-Key: $YOUR_API_KEY" \ -F "image=@portrait.jpg" \ -F "audio=@voiceover.mp3" \ -F "webhookUrl=https://your.app/hooks/vidgen"
One job per user at a time. Submitting a new job while a previous one is still PENDING or PROCESSING returns 409. Wait for it to finish or cancel it first.
Liveness probe. Returns 200 OK when the API process is up. No authentication required.
{ "status": "ok" }Submit a new video generation job. Send a multipart/form-data request with two files: an image and an audio track. Optionally include a webhookUrl to be notified when the job finishes.
| Field | Type | Description |
|---|---|---|
| image required | file | Single source image. Accepts image/jpeg, image/png, image/webp. Max 50 MB. |
| audio required | file | Voiceover or audio track. Accepts mp3, wav, m4a, ogg. Max 50 MB. |
| webhookUrl optional | string | HTTPS URL that receives a signed callback when the job completes or fails. See Webhooks. |
{
"message": "Job created",
"jobId": "clxxxxx0000abcd1234efgh"
}List your jobs in reverse chronological order. Cursor-paginated.
| Param | Type | Description |
|---|---|---|
| limit optional | integer | Page size, 1–100. Default 20. |
| cursor optional | string | The nextCursor from the previous response. Omit on first page. |
| status optional | string | Filter to one of PENDING, PROCESSING, COMPLETED, FAILED, CANCELLED. |
{
"jobs": [
{
"id": "clxxxxx0000abcd1234efgh",
"status": "COMPLETED",
"outputVideo": "https://…/video.mp4",
"outputShareLink": "https://…/share/…",
"errorMessage": null,
"retries": 0,
"createdAt": "2026-05-07T12:34:56.000Z",
"updatedAt": "2026-05-07T12:38:11.000Z",
"webhookUrl": null
}
],
"pagination": {
"limit": 20,
"nextCursor": "clxxxxx0001…",
"hasNextPage": true
}
}Fetch the current status of a single job, including progress (0–100) when actively processing.
| Param | Type | Description |
|---|---|---|
| jobId required | string | The jobId returned when you submitted the job. |
{
"jobId": "clxxxxx0000abcd1234efgh",
"status": "PROCESSING",
"progress": 62,
"outputVideo": null,
"outputShareLink": null,
"errorMessage": null,
"createdAt": "2026-05-07T12:34:56.000Z",
"updatedAt": "2026-05-07T12:36:08.000Z"
}PENDING — queued, not yet picked upPROCESSING — actively rendering; progress reflects an estimateCOMPLETED — done; outputVideo is populatedFAILED — terminal failure; errorMessage explains whyCANCELLED — you cancelled it before it ranCancel a job. Only works while the job is still in PENDING state — once a worker picks it up, it can no longer be cancelled.
{ "success": true, "message": "Job cancelled successfully" }If you supplied a webhookUrl when creating a job, we'll POST a JSON payload to that URL when the job completes or fails. The body looks like the /api/status/:jobId response.
Every webhook is signed with HMAC-SHA256 using your account's webhook secret. The signature lives in the X-Webhook-Signature header. Reject any request where the signature doesn't match the body — anyone could otherwise forge events to your endpoint.
// Node.js / Express import crypto from 'crypto'; app.post('/hooks/vidgen', express.raw({ type: 'application/json' }), (req, res) => { const got = req.header('X-Webhook-Signature') || ''; const expected = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.body) .digest('hex'); if (got.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) { return res.status(401).end(); } const event = JSON.parse(req.body.toString()); // event.status, event.outputVideo, … res.sendStatus(200); });
HTTPS only. The webhookUrl must start with https://. Plain-HTTP URLs are rejected at job creation.
The API uses standard HTTP status codes. Error responses include a JSON body with an error field describing the problem.
| Code | Meaning |
|---|---|
| 400 | Bad request — missing field, malformed body, or invalid webhookUrl. |
| 401 | Missing X-API-Key header. |
| 401 | API key not recognized or has been deactivated. |
| 404 | The requested job doesn't exist or doesn't belong to you. |
| 409 | You already have an active job, or the job isn't in a cancellable state. |
| 413 | Uploaded file exceeded 50 MB. |
| 415 | Uploaded file's MIME type isn't supported. |
| 429 | Rate limit exceeded — slow down. |
| 500 | Server error. Retry with exponential backoff. |
Per-IP rate limits, returned over standard RateLimit-* response headers.
| Scope | Limit |
|---|---|
All /api/* | 600 requests / minute |
POST /api/jobs | 30 submissions / minute |
You're also capped at 1 active job per API key — submitting another while one is PENDING or PROCESSING returns 409.