---
title: "Transcribe a video for dialogue editing"
method: POST
path: "/v2/transcriptions"
tags: ["Transcriptions"]
---

# Transcribe a video for dialogue editing

`POST /v2/transcriptions`

## Request

- Base URL: `https://api.sync.so`
- URL: `https://api.sync.so/v2/transcriptions`
- Auth: API key in header `x-api-key`

Starts a word-level transcription of a window of a video, the first step of a dialogue edit. Provide exactly one of `sourceVideoUrl` or `sourceAudioUrl`, both hosted in sync. storage (upload via `POST /v2/assets/upload`). Passing pre-extracted audio skips the server-side download and transcode. Poll `GET /v2/transcriptions/{id}` until `status` is `COMPLETED`, then pass the `transcript` and your edits to `POST /v2/dialogue-edits`. An identical source and window returns the existing job instead of starting a new one. Transcription is not billed.

## Request body

- CreateTranscriptionDto
  - `sourceVideoUrl` string, uri: URL of the source video, hosted in sync. storage (upload it via POST /v2/assets/upload first). Provide exactly one of `sourceVideoUrl` or `sourceAudioUrl`.
  - `sourceAudioUrl` string, uri: URL of audio already extracted from the source, hosted in sync. storage. Skips the server-side audio extraction. Provide exactly one of `sourceVideoUrl` or `sourceAudioUrl`.
  - `startMs` integer: Start of the window to transcribe, in milliseconds from the start of the source. Defaults to 0.
  - `endMs` integer: End of the window to transcribe, in milliseconds from the start of the source. Omit to transcribe to the end. The window cannot exceed 60 minutes.
  - `maxSourceSeconds` integer: Refuse the source if it is longer than this many seconds. Useful when a later step, such as a dialogue edit (10 minute window), cannot consume a longer transcript. Cannot exceed the endpoint ceiling of 3600 seconds.
  - `projectId` string, uuid: Optional id of a project in your organization to file the job under.

## Response `201`

- TranscriptionJobDto
  - `id` string, uuid, required: Job id. Poll GET /v2/transcriptions/{id} with it.
  - `revision` string: Content hash of the source and transcript this job produced. Present once the job has a transcript.
  - `status` 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED', required: Job status. `COMPLETED` and `FAILED` are terminal.
  - `sourceVideoUrl` string, nullable, required: The source video the job was created from. Null for jobs created from `sourceAudioUrl`.
  - `sourceStartMs` integer, required: Start of the transcribed window, in source milliseconds.
  - `sourceEndMs` integer, nullable, required: End of the transcribed window, in source milliseconds. Null when the job transcribes to the end of the source.
  - `transcript` object, nullable, required: Word-level transcript. Word timings are in milliseconds from the start of the source video, not the window. Null until `status` is `COMPLETED`.
    - `segments` object[], required
      - `id` string, required
      - `words` object[], required
        - `id` string, required
        - `text` string, required
        - `speakerId` string, nullable
        - `startMs` integer, required
        - `endMs` integer, required
    - `speakers` object[]
      - `id` string, required
      - `displayOrder` integer, required
      - `label` string, required
      - `providerSpeakerId` string
    - `speakerCount` integer, required
  - `speakerCount` integer, nullable, required: Number of distinct speakers detected. Dialogue edits currently support a single speaker; a count above 1 completes the job but a dialogue edit of that source is unsupported. Null until `status` is `COMPLETED`.
  - `difficulty` object, nullable, required: Informational estimate of how hard the source is to edit cleanly, with the reasons behind it. Null while the job is running and for some older jobs. Do not gate behaviour on it.
    - `tier` 'green' | 'yellow' | 'red', required
    - `reasons` string[], required
  - `error` object, nullable, required: Why the job failed. Null unless `status` is `FAILED`.
    - `code` 'NO_AUDIO' | 'NO_AUDIO_TRACK' | 'SOURCE_TOO_LONG' | 'EXTRACTION_FAILED' | 'PROVIDER_FAILED' | 'TIMEOUT' | 'UNKNOWN', required: Machine-readable failure code. `NO_AUDIO`, `NO_AUDIO_TRACK` and `SOURCE_TOO_LONG` describe the source and will not succeed on retry.
    - `message` string, required: Human-readable summary of the failure.
  - `createdAt` string, date-time, required
  - `startedAt` string, date-time, nullable, required
  - `finishedAt` string, date-time, nullable, required

## Other responses

- `402`: The account is on a payment or fraud hold
- `422`: The source is not hosted in sync. storage, or supplied audio is longer than the allowed window (SOURCE_TOO_LONG)
- `429`: Per-caller rate limit, or the daily transcription cap of the organization

## Changes

- **2026-10-14** `731f968d8555`: 3 info
  - added the optional property `revision` to the response with the `201` status
  - added the optional property `transcript/anyOf[subschema #1]/segments/items/words/items/speakerId` to the response with the `201` status
  - added the optional property `transcript/anyOf[subschema #1]/speakers` to the response with the `201` status
- …earlier changes not shown

[Operation history](https://skmtc.dev/sync/apis/sync-api-v2/changes/v2/transcriptions/post.md)

---

[API](https://skmtc.dev/sync/apis/sync-api-v2.md) - [All operations](https://skmtc.dev/sync/apis/sync-api-v2/llms.txt) - [OpenAPI document](https://skmtc.dev/sync/apis/sync-api-v2/revisions/26662c96c138?raw)
