Jobs
A job is one source file processed with one preset.
Create a job
POST /api/v1/jobsRequest body
| Field | Type | Required | Description |
|---|---|---|---|
inputUrl | string | yes | Publicly reachable HTTP or HTTPS URL of the source file |
preset | string | yes | Preset identifier, for example mp4_264_720p |
metadata | object | no | Arbitrary key and value pairs stored with the job |
There is no webhookUrl field. The destination for notifications is the
webhook configured on your account, not something you set per job. Sending
webhookUrl in the body has no effect.
curl -X POST https://videotranscode.cloud/api/v1/jobs \
-H "Authorization: Bearer $VT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputUrl": "https://cdn.example.com/master.mov",
"preset": "hls_abr_264_standard"
}'Response
201 Created.
{
"id": "9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"status": "queued",
"preset": "hls_abr_264_standard",
"inputUrl": "https://cdn.example.com/master.mov",
"webhookUrl": "https://yourapp.example.com/hooks/video",
"createdAt": "2026-09-10T14:08:11.204Z",
"estimatedProcessingTime": "8-18 minutes",
"links": {
"self": "/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"cancel": "/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42"
},
"user": {
"remainingJobs": 97,
"plan": "Indie/Pro"
}
}webhookUrl echoes the webhook currently configured on your account, or null
if you have not configured one. user.plan is the plan's display name, which
is not always the same as the plan identifier used elsewhere; the free plan is
displayed as Starter.
What happens on acceptance
The quota slot is claimed before anything else, so two simultaneous requests cannot both slip past your monthly limit. If the job cannot be handed to the queue, the slot is released and the job is marked failed, so a queueing problem does not cost you a job.
The source is not fetched during this request. URL reachability, file format and duration are all checked later, by the worker. A job that is accepted here can still fail. See Handling failures.
Errors
400 for malformed JSON, a missing or non-URL inputUrl, a missing preset,
or a preset name that does not exist. 403 when the preset exists but your
plan does not include it. 429 when your monthly quota is used up. Bodies for
all of these are in Errors.
Read a job
GET /api/v1/jobs/{id}curl https://videotranscode.cloud/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42 \
-H "Authorization: Bearer $VT_API_KEY"{
"id": "9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"status": "completed",
"preset": "hls_abr_264_standard",
"inputUrl": "https://cdn.example.com/master.mov",
"outputUrl": "https://sfo3.digitaloceanspaces.com/...&X-Amz-Signature=...",
"expiration": "2026-09-17T14:12:47.118Z",
"duration": 742,
"errorMessage": null,
"retryCount": 0,
"metadata": {
"createdAt": "2026-09-10T14:08:11.204Z",
"startedAt": "2026-09-10T14:08:19.663Z",
"completedAt": "2026-09-10T14:12:47.118Z",
"lastCheckedAt": "2026-09-10T14:12:47.118Z"
},
"links": {
"self": "/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"output": "/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42/download-url"
}
}| Field | Description |
|---|---|
status | queued, processing, completed, failed or cancelled |
outputUrl | Signed download link, or null when there is nothing to download |
expiration | When that specific link stops working, or null |
duration | Source duration in seconds, measured during processing |
errorMessage | Why the job failed, or null |
retryCount | How many times the worker retried this job |
metadata | Lifecycle timestamps; null for stages not reached yet |
outputUrl is signed at read time, so it is fresh on every call. It is null
when the job is not complete, when the output is no longer in storage, and for
a small number of old jobs stored before the current storage layout, which can
no longer be signed.
The links.cancel entry appears only while the job is queued or
processing. The links.output entry appears only when there is a signed link
to give you.
List jobs
GET /api/v1/jobsNewest first.
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number; anything that is not a positive integer falls back to the default |
limit | integer | 10 | Items per page, capped at 100 |
status | string | none | Exact match on job status |
preset | string | none | Exact match on preset identifier |
curl "https://videotranscode.cloud/api/v1/jobs?status=completed&limit=25" \
-H "Authorization: Bearer $VT_API_KEY"{
"jobs": [
{
"id": "9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"status": "completed",
"preset": "hls_abr_264_standard",
"inputUrl": "https://cdn.example.com/master.mov",
"outputUrl": "https://sfo3.digitaloceanspaces.com/...&X-Amz-Signature=...",
"expiration": "2026-09-17T14:12:47.118Z",
"duration": 742,
"errorMessage": null,
"createdAt": "2026-09-10T14:08:11.204Z",
"startedAt": "2026-09-10T14:08:19.663Z",
"completedAt": "2026-09-10T14:12:47.118Z",
"links": {
"self": "/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42"
}
}
],
"pagination": {
"page": 1,
"limit": 25,
"totalPages": 2,
"totalCount": 34,
"hasNext": true,
"hasPrev": false
},
"links": {
"self": "/api/v1/jobs?page=1&limit=25",
"next": "/api/v1/jobs?page=2&limit=25",
"prev": null
}
}Listing signs a download link for every completed job on the page. Listing 100 completed jobs therefore does 100 signatures. If you only need statuses, ask for a smaller page.
The list omits retryCount, and it puts the lifecycle timestamps at the top
level rather than under metadata. Reading a single job gives you the fuller
shape.
Cancel a job
DELETE /api/v1/jobs/{id}curl -X DELETE https://videotranscode.cloud/api/v1/jobs/9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42 \
-H "Authorization: Bearer $VT_API_KEY"{
"id": "9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"status": "cancelled",
"message": "Job cancelled successfully",
"cancelledAt": "2026-09-10T14:09:02.551Z"
}Only queued and processing jobs can be cancelled; anything else returns
400. The job row is marked cancelled first and then pulled from the queue, so
a job that had not started never starts. A job already running is marked
cancelled and stops shortly afterwards, and the response says so:
{
"id": "9d1f5c0a-4e7b-4a61-9b2e-8f1c3d5a7e42",
"status": "cancelled",
"message": "Job cancelled; it is currently processing and will stop shortly",
"cancelledAt": "2026-09-10T14:09:02.551Z"
}Cancelling does not return the quota slot. The slot is claimed when the job is created.
Sign a download link
GET /api/v1/jobs/{id}/download-urlReading a job already gives you a signed outputUrl. This endpoint exists for
when you want a link without the rest of the job.
{
"downloadUrl": "https://sfo3.digitaloceanspaces.com/...&X-Amz-Signature=...",
"expiresAt": "2026-09-17T14:12:47.118Z",
"message": "Download URL generated successfully"
}Its errors use a different shape from the rest of the API: a bare message
field with no error. 404 when the job does not exist or is not yours, 400
when the job is not completed or has no output, and 410 when the output can
no longer be signed.
{ "message": "Output file is not available or job is not completed" }