---
title: "Status and result of a queued job"
method: GET
path: "/v1/jobs/{jobId}"
tags: ["Jobs"]
---

# Status and result of a queued job

`GET /v1/jobs/{jobId}`

## Request

- Base URL: `https://api.tryplayhead.com`
- URL: `https://api.tryplayhead.com/v1/jobs/{jobId}`
- Auth: HTTP bearer

Poll this after a call made with `sync: false`. `status` moves `queued` → `running` → `succeeded` or `failed`; the `result` field appears only on success, and holds exactly what the synchronous call would have returned. A failure carries the same actionable error body a synchronous call would have thrown.

## Path parameters

- `jobId` string, required

## Response `200`

The job.

- Job
  - `job_id` string
  - `status` 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled'
  - `operation` 'frames' | 'transcript'
  - `detail` string, nullable
  - `queue` string
  - `credits_charged` number, nullable
  - `cache_hit` boolean
  - `queued_at` string, date-time
  - `started_at` string, date-time, nullable
  - `finished_at` string, date-time, nullable
  - `error` ApiError: Errors are written to be acted on, not just logged. Where a different call would have worked, `suggestion` holds it — ready to spread straight back into the request.
    - `type` string, required: The machine-readable discriminator: `gone`, `source_unavailable`, `insufficient_credits`, `rate_limited`, `invalid_request`, `unauthorized`, `insufficient_scope`, `tier_limit`, `video_not_found`, `no_video_stream`, `processing_failed`.
    - `message` string, required
    - `reason` string: The narrow code under `type`, when one type covers several causes. On `source_unavailable` it is why the platform did not serve the video: `unavailable` (removed, private or a wrong address), `auth` (a signed-in account only), `geo`, `no_video` (the post holds a still picture), `empty` (the page came without the video), `bot`, `ratelimit`, `unknown`. Each one is a different next step, and the code is stable where the message is not.
    - `suggestion` object: Arguments for a call that would work instead.
    - `alternative` string: A different approach, in prose, when no single call is the answer.
  - `result` TranscriptResult
    - `language` string, nullable
    - `source` 'captions' | 'transcribed': `captions` when the uploader wrote them, `transcribed` when we heard them. They differ in reliability and that is the caller's business.
    - `text` string
    - `segments` object[]
      - `start` number
      - `end` number
      - `text` string
      - `confidence` number
      - `words` object[]: Present when `words` was true.
        - `start` number
        - `end` number
        - `word` string
        - `confidence` number
    - `speech` object: Whether the audio was speech at all. Music scored over a video transcribes into fluent sentences nobody said; this says so rather than handing back lyrics as dialogue.
      - `is_speech` boolean
      - `confidence` number
      - `reason` string
    - `low_confidence` object[]: Passages the model was unsure of. Settle these against the frames.
      - `start` number
      - `end` number
      - `text` string
      - `confidence` number
    - `job_id` string
    - `status` string
    - `credits_charged` number, nullable
    - `cache_hit` boolean
    - `video` Video
      - `id` string
      - `platform` string: `youtube`, `tiktok`, `instagram`, `x`, `facebook`, `vimeo`, `reddit`, `twitch`, `dailymotion`, `web` for a generically extracted page, or `upload`.
      - `duration` number: Seconds.
      - `width` integer
      - `height` integer
      - `orientation` string
      - `src_fps` number
      - `title` string, nullable
      - `language` string, nullable
      - `engagement` object, nullable: The platform's own public numbers for the post, read when the link was resolved. A count the platform does not give is null, never 0: Instagram gives no views without a login. Null for an upload.
        - `views` integer, nullable
        - `likes` integer, nullable
        - `comments` integer, nullable
        - `reposts` integer, nullable
        - `posted_at` string, nullable: When the post went up: a date and time, or a date when the platform gives only the day.
        - `read_at` string, date-time: When these numbers were read.
      - `content_box` object, nullable: The picture inside the frame when the source was letterboxed. Present means the tiles show this region, not the full frame.
        - `x` integer
        - `y` integer
        - `width` integer
        - `height` integer
      - `carousel` object, nullable: Present when the post is a photo carousel — N stills with no timeline. One second is one slide: second 0 is slide 1. The number is an index wearing the units the rest of the API speaks, not a duration.
        - `slides` integer
        - `audio_seconds` number, nullable
      - `start_timecode` string, nullable: SMPTE timecode of the source's first frame, e.g. `14:36:27:07`. Present on camera and deck formats, absent on everything from the web. A different clock from the elapsed seconds everywhere else in this response — both are true, and neither should be converted into the other.
      - `interlaced` boolean
      - `audio_tracks` integer: Streams, not channels. A camera master routinely has eight.
    - `subtitles` string: Present when `format` was `srt` or `vtt`.

## Other responses

- `401`: Missing, malformed or expired credential.
- `404`: No such job, or it belongs to another organization.

---

[API](https://skmtc.dev/tryplayhead/apis/playhead-api.md) · [All operations](https://skmtc.dev/tryplayhead/apis/playhead-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tryplayhead/apis/playhead-api/revisions/21dfb775b852?raw)
