Jobs
A Job is the core unit of work in Transcodely. Each job takes an input video, applies one or more encoding configurations, and produces transcoded output files. Jobs support multiple outputs, real-time progress tracking, cost estimation, and delayed-start workflows.
Creating a Job
A minimal job requires an input source, an output origin, and at least one output specification:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"input_url": "gs://my-bucket/uploads/video.mp4",
"output_origin_id": "ori_x9y8z7w6v5",
"outputs": [
{
"type": "mp4",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" }
]
}
],
"priority": "standard"
}'const job = await client.jobs.create({
inputUrl: "gs://my-bucket/uploads/video.mp4",
outputOriginId: "ori_x9y8z7w6v5",
outputs: [
{
type: OutputFormat.MP4,
video: [
{
codec: VideoCodec.H264,
resolution: Resolution.RESOLUTION_1080P,
quality: QualityTier.STANDARD,
},
],
},
],
priority: JobPriority.STANDARD,
});job = client.jobs.create(
input_url="gs://my-bucket/uploads/video.mp4",
output_origin_id="ori_x9y8z7w6v5",
outputs=[{
"type": "mp4",
"video": [{"codec": "h264", "resolution": "1080p", "quality": "standard"}],
}],
priority="standard",
)job, err := client.Jobs.Create(ctx, &transcodely.JobCreateParams{
InputUrl: "gs://my-bucket/uploads/video.mp4",
OutputOriginId: proto.String("ori_x9y8z7w6v5"),
Outputs: []*transcodely.OutputSpec{{
Type: transcodely.OutputFormatMP4,
Video: []*transcodely.VideoVariant{{
Codec: transcodely.VideoCodecH264,
Resolution: transcodely.Resolution1080P,
Quality: transcodely.QualityTierStandard,
}},
}},
Priority: transcodely.JobPriorityStandard,
})You can also use an Origin for the input source instead of a direct URL:
{
"input_origin_id": "ori_input123",
"input_path": "uploads/video.mp4",
"output_origin_id": "ori_output456",
"outputs": [ ... ]
}Job Status Lifecycle
Jobs progress through a well-defined state machine:
| Status | Description |
|---|---|
pending | Job is queued, waiting for a worker |
probing | Analyzing the input file with ffprobe |
awaiting_confirmation | Delayed-start jobs pause here for cost review |
processing | Actively encoding outputs |
completed | All outputs finished successfully |
partial | Some outputs completed, others failed |
failed | Job failed with an error |
canceled | Job was canceled by the user |
State Transitions
pending → probing → processing → completed
↘ ↘ partial
awaiting_confirmation ↘ failed
(delayed start)
Any non-terminal state → canceled (via Cancel)Terminal states are completed, partial, failed, and canceled. Once a job reaches a terminal state, it cannot change further.
Output Specifications
Each job can have up to 10 outputs. Outputs can be defined inline or reference a Preset:
Inline Output
Spell out each output’s format, codec, resolution, and quality directly on the request — self-contained, with nothing to set up in advance.
{
"outputs": [
{
"type": "mp4",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" }
]
},
{
"type": "webm",
"video": [
{ "codec": "vp9", "resolution": "720p", "quality": "economy" }
]
}
]
}Preset Reference
Reference a saved Preset by slug or ID instead, so the encoding settings live in one place and stay consistent across jobs.
{
"outputs": [
{ "preset": "h264_1080p_standard" },
{ "preset": "pst_x9y8z7w6v5" }
]
}Adaptive Streaming (HLS/DASH)
For adaptive bitrate streaming, define multiple video variants in a single output:
{
"outputs": [
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "standard" }
],
"segments": { "duration": 6 },
"hls": { "segment_format": "fmp4" }
}
]
}Output Status
Each output within a job has its own status and progress:
| Status | Description |
|---|---|
pending | Waiting to be processed |
processing | Currently encoding |
completed | Successfully finished |
failed | Encoding failed |
canceled | Canceled before completion |
The overall job progress is the average of all output progresses.
Priority
Jobs support three priority levels that affect processing order — which worker instances are selected and where the job sits in the queue. Priority has no effect on cost; pricing is driven by codec, resolution, framerate, and quality.
| Priority | Use Case |
|---|---|
economy | Batch processing, non-urgent work |
standard | Normal workflow |
premium | Time-sensitive, highest priority |
Delayed Start
For cost-sensitive workflows, use delayed start to review the estimated cost before encoding begins:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"input_url": "gs://my-bucket/expensive-4k-video.mp4",
"output_origin_id": "ori_x9y8z7w6v5",
"outputs": [ ... ],
"delayed_start": true
}'const job = await client.jobs.create({
inputUrl: "gs://my-bucket/expensive-4k-video.mp4",
outputOriginId: "ori_x9y8z7w6v5",
outputs: [/* ... */],
delayedStart: true,
});job = client.jobs.create(
input_url="gs://my-bucket/expensive-4k-video.mp4",
output_origin_id="ori_x9y8z7w6v5",
outputs=[...],
delayed_start=True,
)job, err := client.Jobs.Create(ctx, &transcodely.JobCreateParams{
InputUrl: "gs://my-bucket/expensive-4k-video.mp4",
OutputOriginId: proto.String("ori_x9y8z7w6v5"),
Outputs: []*transcodely.OutputSpec{ /* ... */ },
DelayedStart: true,
})With delayed_start: true, the job follows this flow:
pending— Job is queuedprobing— Input file is analyzedawaiting_confirmation— Job pauses with cost estimate- You review
total_estimated_costand per-output pricing - Call
Confirmto proceed, orCancelto abort
# Confirm the job after reviewing costs
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Confirm
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{ "id": "job_a1b2c3d4e5f6" }'const job = await client.jobs.confirm("job_a1b2c3d4e5f6");job = client.jobs.confirm(id="job_a1b2c3d4e5f6")job, err := client.Jobs.Confirm(ctx, "job_a1b2c3d4e5f6")Clipping and trimming
Encode only a sub-range of the input instead of the whole file by setting clip. start_seconds defaults to 0 (the start of the input); omit end_seconds (or set it to 0) to encode through to the end of the input. When end_seconds is set it must be at least 1 ms greater than start_seconds (sub-millisecond windows are rejected). Both bounds are stored to 0.1 ms precision, and { "start_seconds": 0, "end_seconds": 0 } normalizes to no clip.
{
"input_url": "gs://my-bucket/interview.mp4",
"output_origin_id": "ori_x9y8z7w6v5",
"outputs": [
{
"type": "hls",
"video": [{ "codec": "h264", "resolution": "1080p" }]
}
],
"clip": {
"start_seconds": 2,
"end_seconds": 7
}
}const job = await client.jobs.create({
inputUrl: "gs://my-bucket/interview.mp4",
outputOriginId: "ori_x9y8z7w6v5",
outputs: [/* ... */],
clip: { startSeconds: 2, endSeconds: 7 },
});job = client.jobs.create(
input_url="gs://my-bucket/interview.mp4",
output_origin_id="ori_x9y8z7w6v5",
outputs=[...],
clip={"start_seconds": 2, "end_seconds": 7},
)job, err := client.Jobs.Create(ctx, &transcodely.JobCreateParams{
InputUrl: "gs://my-bucket/interview.mp4",
OutputOriginId: proto.String("ori_x9y8z7w6v5"),
Outputs: []*transcodely.OutputSpec{ /* ... */ },
Clip: &transcodely.ClipConfig{StartSeconds: 2, EndSeconds: 7},
})Clipping applies job-wide: every output is encoded from the clipped range, and cuts are frame-accurate (outputs are always re-encoded). Thumbnails and sprites are computed within the clip window, so thumbnail timestamps are relative to the clipped output, not the original input. Billing keys off the produced output duration, so a clip costs less than encoding the full input — plus the flat per-job processing fee, which is the same for a short clip as for a full encode.
A clip range that falls outside the input’s real duration is caught after probing: the job fails with error code input_clip_out_of_range.
Real-Time Watching
Use the Watch streaming endpoint to receive live updates as a job progresses:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Watch
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{ "id": "job_a1b2c3d4e5f6" }'for await (const event of client.jobs.watch("job_a1b2c3d4e5f6")) {
console.log(`${event.event}: ${event.job?.progress}%`);
if (event.event === WatchEventType.COMPLETED) break;
}for event in client.jobs.watch(id="job_a1b2c3d4e5f6"):
print(f"{event.event}: {event.job.progress}%")
if event.event == "completed":
breakstream := client.Jobs.Watch(ctx, "job_a1b2c3d4e5f6")
defer stream.Close()
for stream.Next() {
event := stream.Current()
fmt.Printf("%s: %d%%\n", event.GetEvent(), event.GetJob().GetProgress())
if event.GetEvent() == transcodely.WatchEventCompleted {
break
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}The stream sends events as the job progresses:
| Event | Description |
|---|---|
snapshot | Initial full state on connect |
progress | Progress percentage changed |
status_change | Status transitioned (e.g., pending to processing) |
completed | Terminal state reached — stream closes after this |
heartbeat | Periodic keepalive (every 3 seconds) |
The Watch stream automatically closes when the job reaches a terminal state (completed, failed, canceled, or partial).
Metadata
Attach custom key-value metadata to jobs for your own tracking:
{
"metadata": {
"user_id": "usr_12345",
"campaign": "summer-2026",
"source": "upload-api"
}
}See Metadata for constraints and usage patterns.
Canceling a Job
Cancel a job that is in a non-terminal state:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Cancel
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{ "id": "job_a1b2c3d4e5f6" }'const job = await client.jobs.cancel("job_a1b2c3d4e5f6");job = client.jobs.cancel(id="job_a1b2c3d4e5f6")job, err := client.Jobs.Cancel(ctx, "job_a1b2c3d4e5f6")For jobs in processing state, outputs that have already completed will retain their completed status. In-progress outputs are canceled, and you are billed the flat processing fee plus the outputs that finished. Canceling a job that is still pending — before an encoder picks it up — costs nothing at all.
Cost Tracking
Every job includes cost fields that are populated at different stages:
| Field | Populated At | Description |
|---|---|---|
total_estimated_cost | After probing | Sum of all output estimated costs |
total_actual_cost | After completion | Sum of actual costs (based on encoded duration) |
currency | At creation | ISO 4217 currency code (currently always EUR) |
Per-output costs are available in outputs[].estimated_cost and outputs[].actual_cost. For ABR outputs with multiple variants, see variant_pricing[] for per-variant cost breakdowns.