DocsAPI reference

API reference

Every /v1 endpoint with parameters and examples. Download the OpenAPI spec as JSON or YAML.

Introduction

Base URL
https://api.transcript1.com/v1

The base URL is https://api.transcript1.com/v1.

  • Request and response bodies are JSON with snake_case field names.
  • Timestamps are ISO 8601 in UTC, for example 2026-10-13T09:30:00.000Z.
  • Durations and transcript times are in seconds.
  • Every error uses the same structure. See Errors.
  • Every response to an authenticated request carries X-RateLimit-Limit and X-RateLimit-Remaining. See Billing and limits.
  • source=auto prefers usable original-language captions on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn, otherwise speech-to-text. Not every video has captions. source=captions never downloads audio or falls back to STT; no usable track means no_captions. source=stt and speaker labels bypass captions. See the source capability matrix for retrieval restrictions and segment versus word timing.

Download the machine-readable OpenAPI spec:

Authentication

curl https://api.transcript1.com/v1/account \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"

Send a credential in the Authorization header: Authorization: Bearer <token>.

  • An API key starts with t1_. Create one on the API keys page. API keys can call every endpoint. POST /v1/topups charges the saved card only for a key with Can buy top-ups switched on; it is off by default.
  • An OAuth access token starts with t1o_. MCP clients such as Claude and ChatGPT receive one when you connect them. Its scopes are transcriptions:read and transcriptions:write, plus billing:write only when you allow it.
ScopeAllows
transcriptions:readGET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload and GET /v1/account
transcriptions:writePOST /v1/transcriptions, DELETE /v1/transcriptions/:id, POST /v1/transcriptions/:id/upload/complete and POST /v1/topups
billing:writeLets POST /v1/topups charge the saved card, up to the monthly agent limit. Never granted by default: a client must ask for it, the consent screen shows it unchecked, and you can switch it on or off later with Can buy top-ups on the Connected apps page. Without it, POST /v1/topups returns 402 topup_needs_user.
  • A token without the required scope returns 403 insufficient_scope.
  • A missing or invalid credential returns 401 unauthorized.

For chunk uploads, authenticate PUT /v1/transcriptions/:id/upload/chunks/:index with X-Upload-Token. You receive this token when you create the upload. See Uploads.

Idempotency

curl -X POST https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Idempotency-Key: 5f1c2b0e-8d4a-4c3e-9b7a-2e6f1d0c9a8b" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

To retry POST /v1/transcriptions safely, send an Idempotency-Key request header.

The value must be 1 to 255 characters. Send it as a header, not a query parameter.

  • The same key and parameters return the original transcription without another charge.
  • The same key with different parameters returns 409 idempotency_conflict.
  • For chunked uploads, repeating the create request with the same key issues a fresh upload token.

Errors

402 Payment Required
{
  "error": {
    "code": "insufficient_minutes",
    "message": "Not enough minutes",
    "retryable": false,
    "action_url": "https://transcript1.com/dashboard/billing",
    "minutes_needed": 20,
    "minutes_remaining": 3
  }
}
  • A request error means the API refused the call.
  • A job failure means the API accepted the transcription, then it failed. Its status is failed.

The error envelope

Every /v1 request error contains an error object with code, message and retryable.

  • Handle errors by code.
  • Display message to the user. Its wording may change.
  • When retryable is true, the same request may succeed later.

Some errors add fields next to retryable:

FieldOn which errorsMeaning
action_urlinsufficient_minutes, no_active_plan, topup_needs_userWhere to buy a plan or a top-up.
minutes_needed, minutes_remaininginsufficient_minutesMinutes the request needs (null when it is not known yet) and minutes you have.
reason, packstopup_needs_userWhy the card was not charged, and the packs that were requested.
supported_platformsunsupported_platformThe platforms you can send: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"].
missing_chunksupload_incompleteIndexes of the chunks that did not arrive.
max_bytesmedia_too_largeThe largest file size allowed for you, in bytes.
paramsinvalid_requestFor body and query validation errors, an object mapping each field to what is wrong with it.

Request errors

HTTP statuscodeRetryableWhen
400, 422invalid_requestNoA parameter or body field is missing or invalid, for example diarize together with source: "captions", or reuse_recent with webhook_url. Also returned when the request body is not valid JSON.
422invalid_urlNoUse a YouTube, X, TikTok, Instagram, Facebook or Bluesky link, or another platform listed in Sources and options. Also returned when platform does not match the link.
422unsupported_platformNoplatform is not supported.
422unsupported_mediaNoThe uploaded file does not have a supported extension.
422unsupported_translationNoThe translate_to language of a live session is not supported, or live translation is not available.
401unauthorizedNoThe credential is missing, invalid, expired or revoked, or the upload token is invalid or expired.
403insufficient_scopeNoAn OAuth token lacks transcriptions:read or transcriptions:write.
403email_not_verifiedNoVerify your email address before creating transcriptions.
402insufficient_minutesNoYour usable minutes are 0 or fewer than the transcription needs. When auto top-up is on, transcript1 tries to top up first. Buy minutes at action_url, or let an agent buy them with POST /v1/topups or the MCP tool top_up.
402no_active_planNoYou tried to buy a top-up without an active paid plan. POST /v1/topups and the dashboard's billing endpoints return this, not the transcription endpoints.
402topup_needs_userOnly for pending and in_progressPOST /v1/topups did not charge the card. reason says why: not_allowed (the credential cannot buy top-ups, or its user is not a team admin), cap_reached (the monthly agent limit has no room), card_declined, authentication_required or no_card. The user can buy the packs at action_url. For pending (the payment is not confirmed yet) and in_progress (another top-up is running), the charge may still complete: retry later with the same Idempotency-Key instead of sending the user to buy.
404not_foundNoThe transcription does not exist or belongs to another team.
409upload_incompleteNoSome chunks are missing. Send them and complete again.
409invalid_stateNoThe transcription is not awaiting an upload, so chunks can no longer be sent or completed.
409idempotency_conflictNoThe Idempotency-Key was already used with different parameters.
409session_endedNoA live session has ended, so POST /v1/live/sessions/:id/token cannot issue a token. Also sent as a WebSocket error event.
413media_too_largeNoThe file is larger than your limit. See Uploads.
413invalid_requestNoA JSON body over 1 MB. Files use media_too_large instead.
429rate_limitedYesToo many requests per minute. Wait for Retry-After seconds.
429too_many_active_jobsYesToo many transcriptions running, or too many open uploads. Wait for Retry-After seconds.
429too_many_live_sessionsYesYour plan's simultaneous live sessions are in use. Wait for Retry-After seconds (30).
500internal_errorYesSomething went wrong on our side. Retry shortly.
503service_unavailableYestranscript1 is temporarily unavailable. Retry shortly.
503live_capacityYesAll live capacity is in use. Wait for Retry-After seconds (30) and create the session again.
503live_unavailableNoLive transcription is not available right now.

Browser requests authenticated with a login cookie also have their origin checked. A request from another site returns 403 forbidden_origin.

API calls with an Authorization header are not affected.

Every 429 response includes a Retry-After header. Its value is the number of seconds to wait. See Billing and limits for the limits.

Retry a request

  • Retry only when retryable is true: rate_limited, too_many_active_jobs, internal_error, service_unavailable, and topup_needs_user with reason pending or in_progress.
  • Follow Retry-After when present. Otherwise, increase the wait between attempts.
  • When retrying POST /v1/transcriptions or POST /v1/topups, send the same Idempotency-Key header. This avoids a second charge if the original request succeeded.
  • For every other code, fix the request before sending it again.

Job failures

A transcription with status set to failed includes an error object with code and message.

Failures can happen after the request returns. Read error when you poll or receive a transcription.failed webhook. A failed translation sends a transcription.translation.failed webhook and never fails the transcript.

error.codeMeaningTry again?
media_unavailableThe video does not exist or is no longer available.No. Check the URL.
media_privateThe video is private.No. Use a public post or a public or unlisted YouTube video.
login_requiredThe platform requires a logged-in account.No. Permanent failure; not retried.
no_audioThe video has no sound track.No. Permanent failure; not retried.
age_restrictedThe video is age-restricted.No. Upload the file instead.
live_streamThe video is a live stream, or a stream that has not started yet.After the stream has ended.
no_captionssource was captions and the video has no usable caption track.Use source: "auto" or "stt".
media_too_longSpeech-to-text accepts up to 5 hours of media.No. Split the media.
unsupported_mediaThe file could not be read or has no audio track.No. Send a supported file.
no_speechNo speech was detected.No.
unsupported_languageThe language of this media is not supported.No. Set language to a supported code or "auto".
insufficient_minutesYour minutes ran out before the transcription could be charged, and auto top-up, if on, could not add more.After you top up.
fetch_failedFetching the media failed after several attempts.Yes, later.
transcription_failedSpeech-to-text failed after several attempts.Yes, later.

Failed and canceled transcriptions are never charged. Any minutes already charged are refunded when the transcription fails or is canceled.

The Transcription object

The Transcription object
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "completed",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": {
    "title": "Example video",
    "author": "Example channel",
    "author_url": "https://www.youtube.com/@examplechannel",
    "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
    "duration_seconds": 212
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": "youtube_captions",
  "caption_kind": "auto_generated",
  "language": "en",
  "billed_minutes": 4,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": "2026-10-13T09:30:04.000Z",
  "deleted_at": null,
  "transcript": {
    "text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
    "speakers": [],
    "segments": [
      {
        "start": 0,
        "end": 4.2,
        "speaker": null,
        "language": "en",
        "text": "Welcome back to the channel."
      },
      {
        "start": 4.2,
        "end": 9.8,
        "speaker": null,
        "language": "en",
        "text": "Today we are looking at how transcripts are made."
      }
    ]
  }
}

Attributes

  • group_idstringrequirednullable

    Public transcription group ID, or null for legacy ungrouped work.

  • idstringrequired
  • objectstringrequired

    Allowed valuestranscription

  • statusstringrequired

    Allowed valuesawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceledlive

  • sourceobjectrequired
    Show child attributes
    With url
    • platformstringrequired
    • urlstringrequirednullable
    • media_idstringrequirednullable
    With filename
    • platformstringrequired

      Allowed valuesupload

    • filenamestringrequirednullable
    • sizenumberrequirednullable
    With session_id
    • platformstringrequired

      Allowed valueslive

    • session_idstringrequirednullable
  • mediaobjectrequirednullable
    Show child attributes
    • titlestringoptionalnullable
    • authorstringoptionalnullable
    • author_urlstringoptionalnullable
    • thumbnail_urlstringoptionalnullable
    • filenamestringoptional
    • mime_typestringoptional
    • sizenumberoptional
    • duration_secondsnumberoptionalnullable
  • optionsobjectrequired
    Show child attributes
    • clipobjectoptionalnullable
      Show child attributes
      • startnumberrequired
      • endnumberrequired
      • requested_endnumberoptional
    • chunkobjectoptional
      Show child attributes
      • audio_startnumberrequired
    • sourcestringrequired

      Allowed valuesautocaptionsstt

    • languagestringrequired
    • diarizebooleanrequired
    • translate_tostringoptional
  • transcript_sourcestringrequirednullable

    Allowed valuesyoutube_captionsx_captionstiktok_captionsbluesky_captionsdailymotion_captionslinkedin_captionsspeech_to_textlive

  • caption_kindstringrequirednullable

    Null until resolved, for speech-to-text, or when native caption metadata does not establish authorship. Unknown does not mean manual.

    Allowed valuesmanualauto_generated

  • languagestringrequirednullable
  • billed_minutesintegerrequirednullable
  • cachedbooleanrequired
  • errorobjectrequirednullable
    Show child attributes
    • codestringrequired
    • messagestringrequirednullable
  • created_atstring · date-timerequirednullable
  • completed_atstring · date-timerequirednullable
  • deleted_atstring · date-timerequirednullable

    When the transcription was deleted. Always null unless the request includes include_deleted=true.

  • translationobjectoptionalnullable

    The translation this read applies: the requested translation_language, else the translate_to language, else the most recent one, or null. Included in GET responses.

    Show child attributes
    • languagestringrequired
    • statusstringrequired

      Allowed valuesprocessingcompletedfailed

    • errorstringoptional
    • same_as_sourcebooleanoptional

      true on a completed translation into the language the transcript is already in. Nothing was translated: its text is the original text, and text, SRT and WebVTT show each line once.

  • translationsarray of objectsoptional

    Every translation of the transcription, oldest first, including a pending translate_to language. Included in GET and list responses.

    Show child attributes
    • languagestringrequired
    • statusstringrequired

      Allowed valuesprocessingcompletedfailed

    • errorstringoptional
    • same_as_sourcebooleanoptional

      true on a completed translation into the language the transcript is already in. Nothing was translated: its text is the original text, and text, SRT and WebVTT show each line once.

  • transcriptobjectoptional

    Included in GET responses for completed transcriptions.

    Show child attributes
    • textstringrequired
    • speakersarray of stringsrequired
    • segmentsarray of objectsrequired
      Show child attributes
      • startnumberrequired
      • endnumberrequired
      • speakerstringrequirednullable
      • languagestringrequirednullable
      • textstringrequired
      • translationstringoptional

        Translated text, present with translation=both once a translation is completed.

      • wordsarray of objectsoptionalnullable
        Show child attributes
        • startnumberrequired
        • endnumberrequired
        • textstringrequired
        • confidencenumberoptionalnullable
        • speakerstringrequirednullable

Transcriptions

Create, read, list and delete transcriptions of video links and uploaded files.

Create a transcription

POST /v1/transcriptions

Transcribes an uploaded file or a YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat link. Social posts must be public; only the first video is used.

Twitch accepts clips and completed VODs; Pinterest accepts native video pins; LinkedIn accepts public video posts; Snapchat accepts Spotlight. New platforms require public recorded native media without supplied account cookies; no live streams or external embeds.

platform is optional and detected from the link. Captions are available on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn when a usable native track exists. Dailymotion supports direct SRT, not caption playlists; Bluesky captions require a supported trusted PDS; LinkedIn supports public native posts, not Learning. Other platforms use speech-to-text. Caption kind is null when authorship is unknown. All sources use the same per-minute price.

Returns 200 for a final result, 201 when awaiting an upload and 202 while processing. Pass wait to wait up to 60 seconds for a final status.

Pass reuse_recent=true with a platform URL to reuse a transcription of the same video and options from the last 24 hours. The response includes reused: true, with no additional charge. Short links may not be matched. An Idempotency-Key replay takes precedence.

curl -X POST https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "queued",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": null,
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": null,
  "caption_kind": null,
  "language": null,
  "billed_minutes": null,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": null,
  "deleted_at": null,
  "reused": false
}

Query

  • waitintegeroptionalnullableDefault: 0

    Seconds to wait for a final status before responding, up to 60. Defaults to 0.

  • reuse_recentstringoptionalDefault: false

    For platform URLs only. Cannot be combined with webhook_url. Returns the team's most recent transcription of the same video with the same source, language and diarize. It must have been created in the last 24 hours and must not be failed or canceled. Reuses that transcription without creating or charging for a new one.

    Allowed valuestruefalse

Headers

  • Idempotency-Keystringoptional

    Returns the original transcription when you repeat a request with the same key and parameters.

Body

With url
  • platformstringoptional

    Optional; detected from the link. If supplied, must match the link.

    Allowed valuesyoutubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat

  • urlstringrequired

    YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat video link. Social posts must be public; only the first video is used. New platforms support public recorded native video without supplied account cookies, not live streams or external embeds.

  • sourcestringoptional

    "auto" uses native captions when available, otherwise speech-to-text. "captions" never falls back to speech-to-text and cannot be combined with diarize or uploads. "stt" always uses speech-to-text; diarize with auto skips captions. Captions are available on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn when a usable native track exists. Dailymotion supports direct SRT, not caption playlists; Bluesky captions require a supported trusted PDS; LinkedIn supports public native posts, not Learning. Other platforms use speech-to-text. Caption kind is null when authorship is unknown.

    Allowed valuesautocaptionsstt

  • languagestringoptional
  • diarizebooleanoptional
  • translate_tostringoptional

    Two-letter language code. Translates the transcript once it completes. Translation is free.

  • webhook_urlstring · urioptional
With upload
  • uploadobjectrequired
    Show child attributes
    • filenamestringrequired
    • sizeintegerrequired
    • mime_typestringoptional
  • sourcestringoptional

    Allowed valuesautostt

  • languagestringoptional
  • diarizebooleanoptional
  • translate_tostringoptional

    Two-letter language code. Translates the transcript once it completes. Translation is free.

  • webhook_urlstring · urioptional
multipart/form-data
  • filestring · binaryrequired
  • sourcestringoptional

    Allowed valuesautostt

  • languagestringoptional
  • diarizebooleanoptional
  • translate_tostringoptional

    Two-letter language code. Translates the transcript once it completes. Translation is free.

  • webhook_urlstring · urioptional

Returns

  • 200 OK — The transcription reached a final status
  • 201 Created — The transcription is awaiting a chunked upload. upload describes where to send each chunk.
  • 202 Accepted — The transcription is processing

Other statuses

  • 400 Bad Request — The JSON request body is malformed
  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — Not enough minutes. Applies to every request body format.
  • 403 Forbidden — Insufficient scope, unverified email, cross-site request or a single-member team plan
  • 409 Conflict — Idempotency-Key was reused with different parameters.
  • 413 Content Too Large — The upload or the JSON request body is too large
  • 422 Unprocessable Content — Invalid request, URL, platform or file type
  • 429 Too Many Requests — Too many requests or too many active transcriptions. Wait for the number of seconds in Retry-After.

Get a transcription

GET /v1/transcriptions/{id}

Returns the transcription object. Completed transcriptions include the transcript. Use format to render it as text, SRT or WebVTT.

If you request a rendered format before completion, the response is JSON. It returns 202 while processing or 200 if the transcription failed or was canceled.

With include_deleted=true, a deleted transcription is returned as a record without a transcript. For deleted records, wait is ignored and rendered formats return 404.

curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "completed",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": {
    "title": "Example video",
    "author": "Example channel",
    "author_url": "https://www.youtube.com/@examplechannel",
    "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
    "duration_seconds": 212
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": "youtube_captions",
  "caption_kind": "auto_generated",
  "language": "en",
  "billed_minutes": 4,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": "2026-10-13T09:30:04.000Z",
  "deleted_at": null,
  "transcript": {
    "text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
    "speakers": [],
    "segments": [
      {
        "start": 0,
        "end": 4.2,
        "speaker": null,
        "language": "en",
        "text": "Welcome back to the channel."
      },
      {
        "start": 4.2,
        "end": 9.8,
        "speaker": null,
        "language": "en",
        "text": "Today we are looking at how transcripts are made."
      }
    ]
  }
}

Path

  • idstringrequired

Query

  • waitintegeroptionalnullableDefault: 0

    Seconds to wait for a final status before responding, up to 60. Defaults to 0.

  • wait_forstringoptionalDefault: translation

    What wait waits for. translation also waits for a requested translation to finish, unless translation=none. transcript returns as soon as the transcription itself finishes: a completed transcript may then come with translation.status processing and no translated text yet; text, SRT and WebVTT then show only the original. Defaults to translation.

    Allowed valuestranslationtranscript

  • formatstringoptionalDefault: json

    Allowed valuesjsontextsrtvtt

  • includestringoptional

    Allowed valueswords

  • translationstringoptionalDefault: both

    How to include a completed translation. both adds a translation field to each segment, or a second line in text, SRT and WebVTT. only replaces the text with the translation. none ignores it. Without a completed translation this has no effect. Defaults to both.

    Allowed valuesbothonlynone

  • translation_languagestringoptional

    Two-letter code of the translation to read. Defaults to the translate_to language, then the most recently created translation. Unknown languages return 404 translation_not_found.

  • timestampsstringoptionalDefault: false

    Allowed valuestruefalse

  • include_deletedstringoptionalDefault: false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    Allowed valuestruefalse

Returns

  • 200 OK — The transcription, or the rendered transcript
  • 202 Accepted — A rendered format was requested while the transcription is still processing

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription not found
  • 422 Unprocessable Content — Invalid query parameters
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

List transcriptions

GET /v1/transcriptions

Lists your team's transcriptions, newest first, without transcripts. Pass next_cursor as cursor to get the next page.

Deleted transcriptions are hidden unless you pass include_deleted=true.

curl https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "list",
  "data": [
    {
      "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
      "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
      "object": "transcription",
      "status": "completed",
      "source": {
        "platform": "youtube",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "media_id": "dQw4w9WgXcQ"
      },
      "media": {
        "title": "Example video",
        "author": "Example channel",
        "author_url": "https://www.youtube.com/@examplechannel",
        "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
        "duration_seconds": 212
      },
      "options": {
        "source": "auto",
        "language": "auto",
        "diarize": false
      },
      "transcript_source": "youtube_captions",
      "caption_kind": "auto_generated",
      "language": "en",
      "billed_minutes": 4,
      "cached": false,
      "error": null,
      "created_at": "2026-10-13T09:30:00.000Z",
      "completed_at": "2026-10-13T09:30:04.000Z",
      "deleted_at": null
    }
  ],
  "next_cursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTEwLTEzVDA5OjMwOjAwLjAwMFoiLCJpZCI6IjViMGU2YzFjLTBkNmYtNGMxZi05YjVlLThmM2Y0ZDBhN2ExMSJ9"
}

Query

  • limitintegeroptionalDefault: 20
  • cursorstringoptional
  • statusstringoptional

    Allowed valuesawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceledlive

  • platformstringoptional
  • include_deletedstringoptionalDefault: false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    Allowed valuestruefalse

Returns

  • 200 OK — A page of transcriptions

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 422 Unprocessable Content — Invalid query parameters or cursor
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Cancel or delete a transcription

DELETE /v1/transcriptions/{id}

Cancels an active transcription or one awaiting an upload. Any charged minutes are refunded.

Deletes a completed, failed or canceled transcription together with its stored transcript.

A live transcription cannot be deleted until its live session has ended; before that the request returns 409 invalid_state.

curl -X DELETE https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"

No response body

Path

  • idstringrequired

Returns

  • 204 No Content — Canceled or deleted

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Transcription not found
  • 409 Conflict — The transcription belongs to a live session that has not ended
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Transcribe a buffered part of a video

POST /v1/transcriptions/chunks

Uploads up to 8 MiB of audio for a 1–120 second window. Times are absolute media seconds. Payment settles actual completed coverage; admission does not reserve balance.

curl -X POST https://api.transcript1.com/v1/transcriptions/chunks \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "queued",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": null,
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": null,
  "caption_kind": null,
  "language": null,
  "billed_minutes": null,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": null,
  "deleted_at": null
}

Headers

  • Idempotency-Keystringoptional

Body

  • audiostring · binaryrequired
  • urlstringrequired
  • keystringoptional
  • titlestringoptional
  • durationstringoptional
  • startstringrequired
  • endstringrequired
  • audio_startstringrequired
  • languagestringoptional
  • translate_tostringoptional

Returns

  • 200 OK — Idempotent replay
  • 201 Created — Created, queued or completed

Other statuses

  • 402 Payment Required — Insufficient minutes
  • 409 Conflict — Idempotency conflict
  • 413 Content Too Large — Audio exceeds 8 MiB
  • 422 Unprocessable Content — Invalid media or window
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Uploads

Send a large file in chunks. Start with Create a transcription using an upload body. Send each chunk, then complete the upload.

Upload one chunk

PUT /v1/transcriptions/{id}/upload/chunks/{index}

Sends one chunk of a chunked upload as application/octet-stream.

Authenticate with the X-Upload-Token from the create response. This endpoint does not use a bearer credential.

The body must match the planned chunk size exactly. Sending a chunk again replaces it.

curl -X PUT https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/chunks/0 \
  -H "X-Upload-Token: t1u_Zk3vYb8Nw1ZrT5xLpD0aHcE7sUj9GfMiO2yXtVnR4Bw" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @chunk-0.bin
{
  "object": "upload_chunk",
  "index": 0,
  "size": 838860
}

Path

  • idstringrequired
  • indexintegeroptionalnullable

    Zero-based chunk number from the upload plan

Headers

  • X-Upload-Tokenstringrequired

Body

  • (raw body)string · binaryrequired

Returns

  • 200 OK — The chunk was stored

Other statuses

  • 401 Unauthorized — Missing, invalid or expired upload token
  • 404 Not Found — Transcription not found
  • 409 Conflict — The transcription is no longer awaiting an upload
  • 422 Unprocessable Content — Index outside the chunk plan or wrong body size

Get upload progress

GET /v1/transcriptions/{id}/upload

Lists the received chunks of a chunked upload so an interrupted upload can resume.

curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "upload",
  "status": "awaiting_upload",
  "total_bytes": 48210334,
  "uploaded_bytes": 4194300,
  "received_chunks": [
    0,
    1,
    2,
    3,
    4
  ],
  "chunks": [
    {
      "index": 0,
      "offset": 0,
      "size": 838860
    },
    {
      "index": 1,
      "offset": 838860,
      "size": 838860
    },
    {
      "index": 2,
      "offset": 1677720,
      "size": 838860
    },
    {
      "index": 3,
      "offset": 2516580,
      "size": 838860
    },
    {
      "index": 4,
      "offset": 3355440,
      "size": 838860
    },
    {
      "index": 5,
      "offset": 4194300,
      "size": 8388608
    },
    {
      "index": 6,
      "offset": 12582908,
      "size": 8388608
    },
    {
      "index": 7,
      "offset": 20971516,
      "size": 8388608
    },
    {
      "index": 8,
      "offset": 29360124,
      "size": 8388608
    },
    {
      "index": 9,
      "offset": 37748732,
      "size": 8388608
    },
    {
      "index": 10,
      "offset": 46137340,
      "size": 2072994
    }
  ],
  "expires_at": "2026-10-13T10:30:00.000Z"
}

Path

  • idstringrequired

Returns

  • 200 OK — Upload progress

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription or upload not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Complete a chunked upload

POST /v1/transcriptions/{id}/upload/complete

Checks that every planned chunk arrived with the planned size. Then assembles the file and queues the transcription.

curl -X POST https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/complete \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "queued",
  "source": {
    "platform": "upload",
    "filename": "interview.m4a",
    "size": 48210334
  },
  "media": {
    "filename": "interview.m4a",
    "mime_type": "audio/mp4",
    "size": 48210334
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": null,
  "caption_kind": null,
  "language": null,
  "billed_minutes": null,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": null,
  "deleted_at": null
}

Path

  • idstringrequired

Returns

  • 202 Accepted — The transcription is queued

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — Not enough minutes to queue the transcription. The upload stays awaiting completion.
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Transcription or upload not found
  • 409 Conflict — upload_incomplete means chunks are missing. missing_chunks lists them. invalid_state means the transcription is not awaiting an upload.
  • 429 Too Many Requests — Too many requests or too many active transcriptions. Wait for the number of seconds in Retry-After.

Account

Your plan, remaining minutes, rate limits and top-ups.

Get your account

GET /v1/account

Returns the authenticated user, current plan, remaining transcription minutes and top-up packs. topup.can_charge tells whether POST /topups with this credential charges right away.

curl https://api.transcript1.com/v1/account \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "account",
  "user": {
    "id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
    "email": "[email protected]"
  },
  "plan": {
    "id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
    "name": "Monthly"
  },
  "minutes": {
    "remaining": 1180,
    "cycle": 980,
    "topup": 200,
    "topup_usable": true,
    "resets_at": "2026-11-13T09:30:00.000Z"
  },
  "renews_at": "2026-11-13T09:30:00.000Z",
  "limits": {
    "requests_per_minute": 200,
    "max_active_jobs": 10
  },
  "rates": {
    "batch": 1,
    "live": 1
  },
  "topup": {
    "pack_minutes": 1000,
    "pack_price": 600,
    "currency": "usd",
    "can_charge": true,
    "auto": {
      "enabled": true,
      "threshold_minutes": 100,
      "packs": 1,
      "paused_reason": null
    }
  }
}

Returns

  • 200 OK — The authenticated user's account

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — No account found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Buy top-up minutes

POST /v1/topups

Charges the card on file for top-up packs and adds their minutes to the balance. Pack size and price depend on the plan: read topup from GET /account first.

Charging needs an API key with "Can buy top-ups" switched on, or an OAuth token with billing:write, whose user is a team admin. Agent purchases are limited by the monthly agent limit set on the billing page.

When the card is not charged, 402 topup_needs_user gives the reason. For pending and in_progress the charge may still complete: retry later with the same Idempotency-Key. For the other reasons the user can buy the packs at action_url. Repeat a request with the same Idempotency-Key to get the original top-up without paying again.

curl -X POST https://api.transcript1.com/v1/topups \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"packs":2}'
{
  "object": "topup",
  "status": "succeeded",
  "packs": 2,
  "minutes": 2000,
  "amount": 1200,
  "currency": "usd",
  "card": "visa ****4242",
  "minutes_remaining": 2150
}

Headers

  • Idempotency-Keystringoptional

    Returns the original top-up when you repeat a request with the same key and packs.

Body

  • packsintegeroptionalDefault: 1

    Packs to buy. GET /account shows the pack size and price.

Returns

  • 201 Created — The card was charged and the minutes were added

Other statuses

  • 400 Bad Request — The JSON request body is malformed
  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — topup_needs_user gives a reason. pending (Stripe has not confirmed the payment yet) and in_progress (another top-up is running) are retryable: the charge may still complete, so do not send the user to buy. Retry later with the same Idempotency-Key, or check GET /account. For not_allowed (the credential cannot buy top-ups, or its user is not a team admin), cap_reached (the monthly agent limit has no room), card_declined, authentication_required and no_card, nothing was charged and the user can buy the packs at action_url. no_active_plan means the team is on the Free plan; the user chooses a plan at action_url.
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 409 Conflict — Idempotency-Key was reused with different packs
  • 422 Unprocessable Content — Invalid packs or Idempotency-Key
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

The Account object

The Account object
{
  "object": "account",
  "user": {
    "id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
    "email": "[email protected]"
  },
  "plan": {
    "id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
    "name": "Monthly"
  },
  "minutes": {
    "remaining": 1180,
    "cycle": 980,
    "topup": 200,
    "topup_usable": true,
    "resets_at": "2026-11-13T09:30:00.000Z"
  },
  "renews_at": "2026-11-13T09:30:00.000Z",
  "limits": {
    "requests_per_minute": 200,
    "max_active_jobs": 10
  },
  "rates": {
    "batch": 1,
    "live": 1
  },
  "topup": {
    "pack_minutes": 1000,
    "pack_price": 600,
    "currency": "usd",
    "can_charge": true,
    "auto": {
      "enabled": true,
      "threshold_minutes": 100,
      "packs": 1,
      "paused_reason": null
    }
  }
}

Attributes

  • objectstringrequired

    Allowed valuesaccount

  • userobjectrequired
    Show child attributes
    • idstringrequired
    • emailstringrequired
  • planobjectrequirednullable
    Show child attributes
    • idstringrequired
    • namestringrequired
  • minutesobjectrequired
    Show child attributes
    • remainingnumberrequired
    • cyclenumberrequired
    • topupnumberrequired
    • topup_usablebooleanrequired
    • resets_atstring · date-timerequirednullable
  • renews_atstring · date-timerequirednullable
  • limitsobjectrequired
    Show child attributes
    • requests_per_minutenumberrequired
    • max_active_jobsnumberrequired
  • ratesobjectrequired
    Show child attributes
    • batchnumberrequired
    • livenumberrequired
  • topupobjectrequirednullable

    Top-up packs on the current paid plan. null without a paid plan.

    Show child attributes
    • pack_minutesintegerrequired

      Minutes in one top-up pack

    • pack_priceintegerrequired

      Price of one pack in the smallest currency unit (cents)

    • currencystringrequired

      Lowercase ISO currency code

    • can_chargebooleanrequired

      Whether POST /topups with this credential charges the card on file right away: the credential may buy top-ups, its user is a team admin, a card is on file and the monthly agent limit has room for one pack.

    • autoobjectrequired
      Show child attributes
      • enabledbooleanrequired
      • threshold_minutesintegerrequired

        Auto top-up buys packs when the balance drops below this

      • packsintegerrequired

        Packs each auto top-up buys

      • paused_reasonstringrequirednullable

        Why auto top-up is paused, null while it is not. The user resumes it on the billing page.

        Allowed valuescard_declinedauthentication_requiredno_card

Live

Transcribe audio as it happens. Create a session, then stream PCM audio over a WebSocket with the single-use token and read subtitle lines back.

Create a live session

POST /v1/live/sessions

Starts a live transcription session and returns a single-use WebSocket token. Connect with new WebSocket(websocket_url, ["t1-live", token]) within 60 seconds, then send PCM audio.

Live sessions are billed by connected time: each started minute costs rate balance minutes. Creating a session needs enough balance for the first minute. Read GET /live/capabilities for the languages, translation targets and limits of the active provider.

curl -X POST https://api.transcript1.com/v1/live/sessions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"language":"auto","translate_to":"es","diarize":false,"audio_format":"pcm_s16le","sample_rate":16000,"save":true}'
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
  "status": "waiting",
  "transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/stream",
  "token": "Q2xhdWRlLXRva2VuLWV4YW1wbGUtMzItYnl0ZXMtYmFzZTY0dXJs",
  "token_expires_at": "2026-11-01T12:01:00.000Z",
  "rate": 1
}

Body

  • languagestringoptionalDefault: auto

    auto or a two-letter code the active provider supports. See GET /live/capabilities.

  • translate_tostringoptional

    Two-letter code. Must be a valid target (and pair) for the active provider.

  • diarizebooleanoptionalDefault: false

    Label speakers. Rejected where the provider cannot diarize in that mode.

  • audio_formatstringoptionalDefault: pcm_s16le

    Mono signed 16-bit little-endian PCM. The only supported format.

    Allowed valuespcm_s16le

  • sample_rateintegeroptionalDefault: 16000

    Sample rate of the audio you send, in Hz.

  • savebooleanoptionalDefault: true

    Save the session as a transcription. false keeps no transcript after the session ends.

Returns

  • 201 Created — The session was created. Connect with the token before it expires.

Other statuses

  • 400 Bad Request — The JSON request body is malformed
  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — The usable balance is below the first minute.
  • 403 Forbidden — Insufficient scope, unverified email, cross-site request or a single-member team plan
  • 422 Unprocessable Content — Invalid request, language or translation
  • 429 Too Many Requests — Too many requests or too many live sessions. Wait for the number of seconds in Retry-After.
  • 503 Service Unavailable — Live transcription is at capacity (live_capacity, retryable) or not configured (live_unavailable, not retryable). Retry-After is sent for live_capacity only.

Get a new WebSocket token

POST /v1/live/sessions/{id}/token

Returns a new single-use token for a new connection: to resume after a dropped connection, or to take over from a socket the server still thinks is open. Fails with 409 session_ended once the session has ended.

curl -X POST https://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/token \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "token": "Q2xhdWRlLXRva2VuLWV4YW1wbGUtMzItYnl0ZXMtYmFzZTY0dXJs",
  "token_expires_at": "2026-11-01T12:01:00.000Z",
  "websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/stream"
}

Path

  • idstringrequired

Returns

  • 200 OK — A new token for the session

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Live session not found
  • 409 Conflict — The session has ended
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Get a live session

GET /v1/live/sessions/{id}

Returns the status, options, connected time and minutes charged of a live session of your team.

curl https://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
  "status": "connected",
  "end_reason": null,
  "transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "options": {
    "language": "auto",
    "translate_to": "es",
    "diarize": false,
    "audio_format": "pcm_s16le",
    "sample_rate": 16000,
    "save": true
  },
  "connected_ms": 93400,
  "audio_ms": 91000,
  "minutes_charged": 2,
  "rate": 1,
  "created_at": "2026-11-01T12:00:00.000Z",
  "ended_at": null
}

Path

  • idstringrequired

Returns

  • 200 OK — The live session

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Live session not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Get live capabilities

GET /v1/live/capabilities

Describes the active live provider: supported languages, translation targets, diarization, audio format, sample rates and the rate. Returns { "available": false, "rate": 1 } when live transcription is not configured.

curl https://api.transcript1.com/v1/live/capabilities \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "available": true,
  "auto_language": true,
  "languages": [
    "en",
    "es",
    "de",
    "fr"
  ],
  "translation": {
    "targets": [
      "en",
      "es",
      "de",
      "fr"
    ],
    "pairs": "any",
    "requires_language": false
  },
  "diarize": true,
  "audio_formats": [
    "pcm_s16le"
  ],
  "sample_rate": {
    "min": 8000,
    "max": 48000
  },
  "max_session_minutes": 300,
  "rate": 1
}

Returns

  • 200 OK — The active provider's capabilities

Other statuses

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

The LiveSession object

The LiveSession object
{
  "id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
  "status": "connected",
  "end_reason": null,
  "transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "options": {
    "language": "auto",
    "translate_to": "es",
    "diarize": false,
    "audio_format": "pcm_s16le",
    "sample_rate": 16000,
    "save": true
  },
  "connected_ms": 93400,
  "audio_ms": 91000,
  "minutes_charged": 2,
  "rate": 1,
  "created_at": "2026-11-01T12:00:00.000Z",
  "ended_at": null
}

Attributes

  • idstringrequired
  • statusstringrequired

    Allowed valueswaitingconnecteddisconnectedended

  • end_reasonstringrequirednullable

    Allowed valuesclient_endedbalance_exhaustedidle_timeoutmax_durationdisconnectedupstream_errorinternal_errornot_connected

  • transcription_idstringrequirednullable
  • optionsobjectrequired
    Show child attributes
    • languagestringrequired
    • translate_tostringrequirednullable
    • diarizebooleanrequired
    • audio_formatstringrequired

      Allowed valuespcm_s16le

    • sample_rateintegerrequired
    • savebooleanrequired
  • connected_msintegerrequired
  • audio_msintegerrequired
  • minutes_chargedintegerrequired

    Balance minutes charged so far, after the rate.

  • ratenumberrequired

    Balance minutes charged per started minute of the session.

  • created_atstring · date-timerequirednullable
  • ended_atstring · date-timerequirednullable

The LiveCapabilities object

Groups

Group transcriptions by media, read merged coverage, and manage their lifecycle.

List transcription groups

GET /v1/groups

Returns team-scoped groups, newest update first, using cursor pagination.

curl https://api.transcript1.com/v1/groups \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "data": [
    {
      "id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
      "title": "Example video",
      "url": "https://youtu.be/dQw4w9WgXcQ",
      "platform": "youtube",
      "media_id": "dQw4w9WgXcQ",
      "duration_seconds": 600,
      "covered": [
        [
          0,
          60
        ]
      ],
      "covered_seconds": 60,
      "sources": [
        "download"
      ],
      "languages": [
        "en"
      ],
      "translations": [],
      "members_count": 1,
      "active": false,
      "billed_minutes": 1,
      "created_at": "2026-11-08T10:00:00.000Z",
      "updated_at": "2026-11-08T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

Query

  • limitintegeroptionalDefault: 20
  • cursorstringoptional

Returns

  • 200 OK — Successful response

Other statuses

  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Find a group by media URL

GET /v1/groups/lookup

Resolves the URL and optional page key within your team.

curl "https://api.transcript1.com/v1/groups/lookup?url=%3Curl%3E" \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "title": "Example video",
  "url": "https://youtu.be/dQw4w9WgXcQ",
  "platform": "youtube",
  "media_id": "dQw4w9WgXcQ",
  "duration_seconds": 600,
  "covered": [
    [
      0,
      60
    ]
  ],
  "covered_seconds": 60,
  "sources": [
    "download"
  ],
  "languages": [
    "en"
  ],
  "translations": [],
  "members_count": 1,
  "active": false,
  "billed_minutes": 1,
  "created_at": "2026-11-08T10:00:00.000Z",
  "updated_at": "2026-11-08T10:00:00.000Z"
}

Query

  • urlstringrequired
  • keystringoptional

Returns

  • 200 OK — Successful response

Other statuses

  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Read a group and up to 500 members

GET /v1/groups/{id}

Returns metadata and the newest members with a truncation flag.

curl https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "title": "Example video",
  "url": "https://youtu.be/dQw4w9WgXcQ",
  "platform": "youtube",
  "media_id": "dQw4w9WgXcQ",
  "duration_seconds": 600,
  "covered": [
    [
      0,
      60
    ]
  ],
  "covered_seconds": 60,
  "sources": [
    "download"
  ],
  "languages": [
    "en"
  ],
  "translations": [],
  "members_count": 1,
  "active": false,
  "billed_minutes": 1,
  "created_at": "2026-11-08T10:00:00.000Z",
  "updated_at": "2026-11-08T10:00:00.000Z",
  "members": [],
  "members_truncated": false
}

Path

  • idstringrequired

Returns

  • 200 OK — Successful response

Other statuses

  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Cancel active work and delete a group

DELETE /v1/groups/{id}

Ends live sessions, cancels active batch work and soft-deletes members. Prior paid coverage is retained.

curl -X DELETE https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"

No response body

Path

  • idstringrequired

Returns

  • 204 No Content — Deleted; settled payments and allocations are retained

Other statuses

  • 409 Conflict — New active work joined during deletion; retry
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Read the merged transcript

GET /v1/groups/{id}/transcript

Merges completed members at absolute media timestamps, optionally restricted to a time window.

curl https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX/transcript \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
  "language": null,
  "covered": [
    [
      0,
      60
    ]
  ],
  "segments": []
}

Path

  • idstringrequired

Query

  • fromnumberoptionalnullable
  • tonumberoptional
  • translation_languagestringoptional
  • formatstringoptionalDefault: json

    Allowed valuesjsontextsrtvtt

Returns

  • 200 OK — Merged transcript in the requested format

Other statuses

  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Webhooks

import { Buffer } from 'node:buffer';
import crypto from 'node:crypto';

// header: X-Transcript1-Signature, rawBody: the request body exactly as received,
// secret: your full signing secret including the whsec_ prefix.
export const verifySignature = (secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) => {
  const fields = Object.fromEntries(header.split(',').map(part => part.trim().split('=')));
  const t = Number(fields.t);

  if (!Number.isInteger(t) || !fields.v1 || Math.abs(nowSeconds - t) > 300) {
    return false;
  }

  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');

  return fields.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(fields.v1), Buffer.from(expected));
};
transcription.completed
{
  "type": "transcription.completed",
  "created_at": "2026-10-13T09:30:04.000Z",
  "data": {
    "id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
    "object": "transcription",
    "status": "completed",
    "transcript_source": "youtube_captions",
    "billed_minutes": 4,
    "error": null
  }
}

Pass webhook_url when you create a transcription. transcript1 sends a signed POST to that URL when the transcription completes or fails.

Webhooks are part of the REST API only. The MCP server has no webhook parameter.

Set a webhook URL

Send webhook_url with POST /v1/transcriptions:

  • For a supported video link or chunked upload reservation, add it to the JSON body.
  • For a one-shot upload, use a form field.

The URL must:

  • Use HTTPS.
  • Contain no user name or password.
  • Resolve to a public address.

Private, loopback, link-local and other non-public addresses return 422 invalid_request. The address is checked when you create the transcription and at every delivery.

webhook_url cannot be combined with reuse_recent=true.

Events

typeSent when
transcription.completedThe transcription reached completed.
transcription.failedThe transcription reached failed. data.error has the code and message.
transcription.translation.completedA requested translation reached completed.
transcription.translation.failedA requested translation reached failed.

A canceled transcription sends no webhook. For the two translation events, data also has translation: { language, status, error }. The error is set only when the translation failed. same_as_source: true marks a translation into the language the transcript is already in: nothing was translated, and the event is sent at once. transcription.completed still fires when the transcript is done, and translation events are sent separately when the translation finishes. Deleted transcriptions send no translation events.

Payload

The request body is JSON:

  • created_at is when the event was created.
  • data is the transcription object without transcript.

To read the transcript, call GET /v1/transcriptions/:id with the id from data.

The request includes these headers:

  • Content-Type: application/json
  • User-Agent: transcript1-webhooks/1.0
  • X-Transcript1-Signature, described below.

Verify the signature

Every delivery carries the header X-Transcript1-Signature with the value t=<unix seconds>,v1=<hex digest>.

  • t is the Unix time in seconds when the request was signed.
  • v1 is the hex-encoded HMAC-SHA256 of "<t>.<raw body>". This string contains the value of t, a dot and the request body exactly as received. Use your full signing secret as the key, including the whsec_ prefix.

Verify the raw body bytes before parsing JSON. Parsing and serializing can change whitespace or key order, invalidating the signature.

  • Compare signatures in constant time.
  • Reject requests whose t is too far from the current time. The examples allow 5 minutes.
  • With Express, read the body as raw text so it is not parsed first.
  • With Flask, pass request.get_data(as_text=True) as raw_body.

Your signing secret

Your team's signing secret starts with whsec_. It signs webhooks for every transcription the team creates.

Reveal or rotate it on the Webhooks page.

Rotation replaces the secret immediately. All webhooks sent afterwards use the new secret.

Delivery rules

  • Any 2xx response counts as delivered.
  • Any other status code, connection error or no response within the 10-second timeout counts as a failed attempt.
  • Failed deliveries are retried with exponential backoff, up to 6 attempts in total. The waits are 2, 4, 8, 16 and 32 minutes. The last attempt is about an hour after the first.
  • Use the final URL. transcript1 does not follow redirects, and a 3xx response counts as a failed attempt.
  • Deliveries can arrive more than once. This can happen if your server responds after the timeout. Use data.id and type to ignore duplicates.