Search Documentation
Search across all documentation pages
Jobs

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:

StatusDescription
pendingJob is queued, waiting for a worker
probingAnalyzing the input file with ffprobe
awaiting_confirmationDelayed-start jobs pause here for cost review
processingActively encoding outputs
completedAll outputs finished successfully
partialSome outputs completed, others failed
failedJob failed with an error
canceledJob 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:

StatusDescription
pendingWaiting to be processed
processingCurrently encoding
completedSuccessfully finished
failedEncoding failed
canceledCanceled 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.

PriorityUse Case
economyBatch processing, non-urgent work
standardNormal workflow
premiumTime-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:

  1. pending — Job is queued
  2. probing — Input file is analyzed
  3. awaiting_confirmation — Job pauses with cost estimate
  4. You review total_estimated_cost and per-output pricing
  5. Call Confirm to proceed, or Cancel to 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")

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":
        break
stream := 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:

EventDescription
snapshotInitial full state on connect
progressProgress percentage changed
status_changeStatus transitioned (e.g., pending to processing)
completedTerminal state reached — stream closes after this
heartbeatPeriodic 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 only billed for the encoded portion.

Cost Tracking

Every job includes cost fields that are populated at different stages:

FieldPopulated AtDescription
total_estimated_costAfter probingSum of all output estimated costs
total_actual_costAfter completionSum of actual costs (based on encoded duration)
currencyAt creationISO 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.