DocumentazioneEndpoint API

Endpoint API

Consulta parametri ed esempi per tutti gli endpoint /v1, oppure scarica la specifica OpenAPI in formato JSON o YAML.

Introduzione

URL di base
https://api.transcript1.com/v1

Usa https://api.transcript1.com/v1 come URL di base per le chiamate API.

  • Invia e ricevi body JSON. I nomi dei campi usano snake_case.
  • Le date seguono ISO 8601 in UTC, ad esempio 2026-10-13T09:30:00.000Z.
  • Durata e tempi della trascrizione sono espressi in secondi.
  • Per interpretare gli errori, fai riferimento alla struttura comune.
  • Nelle risposte alle chiamate autenticate trovi sempre X-RateLimit-Limit e X-RateLimit-Remaining. I dettagli sono in Costi e limiti.
  • Con source=auto cerchiamo prima sottotitoli adatti nella lingua originale su YouTube, X, TikTok, Bluesky, Dailymotion e LinkedIn. Se non possiamo usarli, passiamo allo speech-to-text. Non tutti i video hanno sottotitoli. Con source=captions attivi la modalità captions (solo sottotitoli): non scarichiamo audio e non avviamo STT. Senza una traccia adatta ricevi no_captions. Con source=stt o il riconoscimento dei parlanti, usiamo direttamente l'audio. Consulta Link e opzioni di trascrizione per le restrizioni e per sapere quando ricevi timestamp dei segmenti o delle parole.

Puoi scaricare la specifica OpenAPI per usarla nei tuoi strumenti:

Autenticazione

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

Per autenticarti, aggiungi l'header Authorization nel formato Authorization: Bearer <token>.

  • Puoi usare una chiave API, riconoscibile dal prefisso t1_. La crei in “API keys” e puoi usarla con tutti gli endpoint.
  • I client MCP come Claude e ChatGPT ricevono un access token OAuth quando li colleghi. Il prefisso è t1o_ e gli scope sono transcriptions:read e transcriptions:write.
ScopeChiamate consentite
transcriptions:readGET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload e GET /v1/account
transcriptions:writePOST /v1/transcriptions, DELETE /v1/transcriptions/:id e POST /v1/transcriptions/:id/upload/complete
  • Se al token manca lo scope necessario, l'API risponde 403 insufficient_scope.
  • Se non invii una credenziale o non è valida, ricevi 401 unauthorized.

Per inviare i chunk con PUT /v1/transcriptions/:id/upload/chunks/:index, usa invece X-Upload-Token. Lo ricevi all'apertura del caricamento. Segui la procedura in Caricare file.

Idempotenza

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"}'

Quando chiami POST /v1/transcriptions, aggiungi Idempotency-Key: potrai ripetere la chiamata senza creare una seconda trascrizione.

Scegli un valore da 1 a 255 caratteri e invialo nell'header, non nella query.

  • Se riusi chiave e parametri, recuperi la trascrizione della prima chiamata e non paghi di nuovo.
  • Se riusi la chiave ma cambi i parametri, ricevi 409 idempotency_conflict.
  • Per un caricamento in parti, ripetere la chiamata di creazione con la stessa chiave ti dà un nuovo token di caricamento.

Errori

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
  }
}
  • Se l'API rifiuta la chiamata, ricevi l'errore subito nella risposta.
  • Se la chiamata viene accettata ma la trascrizione non riesce durante l'elaborazione, il suo status diventa failed.

Come leggere un errore API

Quando una chiamata /v1 non viene accettata, trovi un oggetto error con code, message e retryable.

  • Nel tuo codice, distingui i casi usando code.
  • Usa message per mostrare la spiegazione all'utente, senza basare la logica sul testo: può cambiare.
  • Con retryable uguale a true, puoi riprovare la stessa chiamata più tardi.

In alcuni casi trovi altri campi oltre a retryable:

CampoCodice associatoA cosa serve
action_urlinsufficient_minutesLink per acquistare un piano o aggiungere minuti con una ricarica.
minutes_needed, minutes_remaininginsufficient_minutesMinuti necessari per la chiamata e saldo disponibile. I minuti necessari valgono null se non li conosciamo ancora.
supported_platformsunsupported_platformElenco delle piattaforme accettate: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"].
missing_chunksupload_incompleteIndici delle parti del file che devi ancora inviare.
max_bytesmedia_too_largeLimite di dimensione del file per il tuo account, in byte.
paramsinvalid_requestNegli errori di validazione del body o della query, associa ogni campo alla spiegazione del problema.

Quando l'API rifiuta la chiamata

Stato HTTPcodePuoi riprovare senza modifiche?Causa
400, 422invalid_requestNoManca un parametro o un campo del body, oppure il valore non è valido. Succede anche con diarize insieme a source: "captions", con reuse_recent insieme a webhook_url o con un body che non contiene JSON valido.
422invalid_urlNoIl link non è valido oppure platform non corrisponde al link. Usa YouTube, X, TikTok, Instagram, Facebook, Bluesky o una delle altre piattaforme in Link e opzioni di trascrizione.
422unsupported_platformNoIl valore di platform non rientra tra quelli accettati.
422unsupported_mediaNoL'estensione del file non è tra quelle supportate.
401unauthorizedNoManca la credenziale, oppure non è valida, è scaduta o è stata revocata. Vale anche per un token di caricamento non valido o scaduto.
403insufficient_scopeNoIl token OAuth non ha lo scope transcriptions:read o transcriptions:write necessario per la chiamata.
403email_not_verifiedNoDevi confermare l'indirizzo email prima di avviare una trascrizione.
402insufficient_minutesNoHai 0 minuti utilizzabili, oppure non bastano per questa trascrizione.
402no_active_planNoPer acquistare una ricarica serve un piano a pagamento attivo. Questo errore arriva dall'endpoint di fatturazione della dashboard, non dagli endpoint di trascrizione.
404not_foundNoNessuna trascrizione corrisponde all'ID, oppure appartiene a un altro team.
409upload_incompleteNoIl file non è completo: invia i chunk mancanti e richiama il completamento.
409invalid_stateNoLa trascrizione non è più in attesa del file. Non puoi inviare altri chunk né completare il caricamento.
409idempotency_conflictNoHai già usato Idempotency-Key con parametri diversi.
413media_too_largeNoLa dimensione del file supera il limite del tuo account. Consulta Caricare file.
413invalid_requestNoIl body JSON supera 1 MB. Per i file, il codice corrispondente è media_too_large.
429rate_limitedSìHai superato il numero di chiamate al minuto. Aspetta quanto indicato da Retry-After, in secondi.
429too_many_active_jobsSìHai troppe trascrizioni in corso o troppi caricamenti aperti. Aspetta quanto indicato da Retry-After, in secondi.
500internal_errorSìSi è verificato un errore nei nostri sistemi. Puoi riprovare tra poco.
503service_unavailableSìIl servizio non è disponibile al momento. Puoi riprovare tra poco.

Se il browser si autentica con un cookie di login, controlliamo anche l'origine della chiamata. Da un altro sito ricevi 403 forbidden_origin.

Il controllo non si applica alle chiamate API con l'header Authorization.

In tutte le risposte 429, l'header Retry-After indica quanti secondi aspettare. Trovi le soglie per piano in Costi e limiti.

Quando e come riprovare

  • Ripeti la chiamata senza modificarla solo con retryable uguale a true: vale per rate_limited, too_many_active_jobs, internal_error e service_unavailable.
  • Aspetta i secondi di Retry-After, se presente. Altrimenti aumenta l'intervallo tra i tentativi.
  • Per ripetere POST /v1/transcriptions, mantieni lo stesso Idempotency-Key. Se la prima chiamata era riuscita, eviti un secondo addebito.
  • Con gli altri codici, risolvi prima il problema della chiamata.

Quando la trascrizione non riesce

Se una trascrizione ha status uguale a failed, trovi il motivo nell'oggetto error, con i campi code e message.

Può succedere anche dopo la risposta alla chiamata iniziale. Controlla error quando leggi lo stato o ricevi il webhook transcription.failed.

error.codeCosa è successoCosa fare
media_unavailableIl video non esiste oppure non è più accessibile.Non riprovare lo stesso link: controlla l'URL.
media_privateNon possiamo accedere al video perché è privato.Usa un post pubblico o un video YouTube pubblico o non in elenco.
login_requiredPer vedere il video serve il login sulla piattaforma.Non riproviamo: è un errore permanente.
no_audioManca la traccia audio del video.Non riproviamo: è un errore permanente.
age_restrictedLa piattaforma applica un limite di età al video.Non riprovare il link; carica il file.
live_streamLa diretta è in corso o deve ancora iniziare.Aspetta che sia finita, poi riprova.
no_captionsHai scelto source uguale a captions, ma manca una traccia di sottotitoli adatta.Passa a source: "auto" o "stt".
media_too_longLa registrazione supera il limite di 5 ore per lo speech-to-text.Dividila in parti più brevi prima di inviarla.
unsupported_mediaNon riusciamo a leggere il file, oppure manca la traccia audio.Invia un file in un formato supportato.
no_speechNessun parlato rilevato.Non riprovare la stessa registrazione.
unsupported_languageNon supportiamo la lingua della registrazione.Correggi language con un codice supportato oppure "auto".
insufficient_minutesAl momento dell'addebito non avevi più abbastanza minuti.Fai una ricarica, poi riprova.
fetch_failedNon siamo riusciti a scaricare la registrazione dopo più tentativi.Puoi riprovare più tardi.
transcription_failedLa trascrizione dell'audio non è riuscita dopo più tentativi.Puoi riprovare più tardi.

Se la trascrizione non riesce o la annulli, non paghi. Se avevamo già scalato dei minuti, li restituiamo al tuo saldo.

L’oggetto Transcription

L’oggetto Transcription
{
  "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."
      }
    ]
  }
}

Campi

  • idstringobbligatorio
  • objectstringobbligatorio

    Valori accettatitranscription

  • statusstringobbligatorio

    Valori accettatiawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • sourceobjectobbligatorio
    Mostra i campi annidati
    With url
    • platformstringobbligatorio
    • urlstringobbligatorioammette null
    • media_idstringobbligatorioammette null
    With filename
    • platformstringobbligatorio

      Valori accettatiupload

    • filenamestringobbligatorioammette null
    • sizenumberobbligatorioammette null
  • mediaobjectobbligatorioammette null
    Mostra i campi annidati
    • titlestringfacoltativoammette null
    • authorstringfacoltativoammette null
    • author_urlstringfacoltativoammette null
    • thumbnail_urlstringfacoltativoammette null
    • filenamestringfacoltativo
    • mime_typestringfacoltativo
    • sizenumberfacoltativo
    • duration_secondsnumberfacoltativoammette null
  • optionsobjectobbligatorio
    Mostra i campi annidati
    • sourcestringobbligatorio

      Valori accettatiautocaptionsstt

    • languagestringobbligatorio
    • diarizebooleanobbligatorio
  • transcript_sourcestringobbligatorioammette null

    Valori accettatiyoutube_captionsx_captionsspeech_to_text

  • caption_kindstringobbligatorioammette null

    Valori accettatimanualauto_generated

  • languagestringobbligatorioammette null
  • billed_minutesintegerobbligatorioammette null
  • cachedbooleanobbligatorio
  • errorobjectobbligatorioammette null
    Mostra i campi annidati
    • codestringobbligatorio
    • messagestringobbligatorioammette null
  • created_atstring · date-timeobbligatorioammette null
  • completed_atstring · date-timeobbligatorioammette null
  • deleted_atstring · date-timeobbligatorioammette null

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

  • transcriptobjectfacoltativo

    Included in GET responses for completed transcriptions.

    Mostra i campi annidati
    • textstringobbligatorio
    • speakersarray of stringsobbligatorio
    • segmentsarray of objectsobbligatorio
      Mostra i campi annidati
      • startnumberobbligatorio
      • endnumberobbligatorio
      • speakerstringobbligatorioammette null
      • languagestringobbligatorioammette null
      • textstringobbligatorio
      • wordsarray of objectsfacoltativoammette null
        Mostra i campi annidati
        • startnumberobbligatorio
        • endnumberobbligatorio
        • textstringobbligatorio
        • confidencenumberfacoltativoammette null
        • speakerstringobbligatorioammette null

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 YouTube/X-only. 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"}'
{
  "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

  • waitintegerfacoltativoammette nullValore predefinito: 0

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

  • reuse_recentstringfacoltativoValore predefinito: 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.

    Valori accettatitruefalse

Headers

  • Idempotency-Keystringfacoltativo

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

Body

With url
  • platformstringfacoltativo

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

    Valori accettatiyoutubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat

  • urlstringobbligatorio

    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.

  • sourcestringfacoltativo

    "auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.

    Valori accettatiautocaptionsstt

  • languagestringfacoltativo
  • diarizebooleanfacoltativo
  • webhook_urlstring · urifacoltativo
With upload
  • uploadobjectobbligatorio
    Mostra i campi annidati
    • filenamestringobbligatorio
    • sizeintegerobbligatorio
    • mime_typestringfacoltativo
  • sourcestringfacoltativo

    Valori accettatiautostt

  • languagestringfacoltativo
  • diarizebooleanfacoltativo
  • webhook_urlstring · urifacoltativo
multipart/form-data
  • filestring · binaryobbligatorio
  • sourcestringfacoltativo

    Valori accettatiautostt

  • languagestringfacoltativo
  • diarizebooleanfacoltativo
  • webhook_urlstring · urifacoltativo

Risposta

  • 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

Altri codici di stato

  • 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"
{
  "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

  • idstringobbligatorio

Query

  • waitintegerfacoltativoammette nullValore predefinito: 0

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

  • formatstringfacoltativoValore predefinito: json

    Valori accettatijsontextsrtvtt

  • includestringfacoltativo

    Valori accettatiwords

  • timestampsstringfacoltativoValore predefinito: false

    Valori accettatitruefalse

  • include_deletedstringfacoltativoValore predefinito: false

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

    Valori accettatitruefalse

Risposta

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

Altri codici di stato

  • 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": [
    {
      "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

  • limitintegerfacoltativoValore predefinito: 20
  • cursorstringfacoltativo
  • statusstringfacoltativo

    Valori accettatiawaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • platformstringfacoltativo
  • include_deletedstringfacoltativoValore predefinito: false

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

    Valori accettatitruefalse

Risposta

  • 200 OK — A page of transcriptions

Altri codici di stato

  • 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.

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

La risposta non contiene un body

Path

  • idstringobbligatorio

Risposta

  • 204 No Content — Canceled or deleted

Altri codici di stato

  • 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
  • 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

  • idstringobbligatorio
  • indexintegerfacoltativoammette null

    Zero-based chunk number from the upload plan

Headers

  • X-Upload-Tokenstringobbligatorio

Body

  • (body originale)string · binaryobbligatorio

Risposta

  • 200 OK — The chunk was stored

Altri codici di stato

  • 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

  • idstringobbligatorio

Risposta

  • 200 OK — Upload progress

Altri codici di stato

  • 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"
{
  "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

  • idstringobbligatorio

Risposta

  • 202 Accepted — The transcription is queued

Altri codici di stato

  • 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 and rate limits.

Get your account

GET /v1/account

Returns the authenticated user, current plan and remaining transcription minutes.

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
  }
}

Risposta

  • 200 OK — The authenticated user's account

Altri codici di stato

  • 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.

L’oggetto Account

L’oggetto Account
{
  "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
  }
}

Campi

  • objectstringobbligatorio

    Valori accettatiaccount

  • userobjectobbligatorio
    Mostra i campi annidati
    • idstringobbligatorio
    • emailstringobbligatorio
  • planobjectobbligatorioammette null
    Mostra i campi annidati
    • idstringobbligatorio
    • namestringobbligatorio
  • minutesobjectobbligatorio
    Mostra i campi annidati
    • remainingnumberobbligatorio
    • cyclenumberobbligatorio
    • topupnumberobbligatorio
    • topup_usablebooleanobbligatorio
    • resets_atstring · date-timeobbligatorioammette null
  • renews_atstring · date-timeobbligatorioammette null
  • limitsobjectobbligatorio
    Mostra i campi annidati
    • requests_per_minutenumberobbligatorio
    • max_active_jobsnumberobbligatorio

Webhook

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
  }
}

Specifica webhook_url alla creazione per ricevere una notifica: transcript1 invia un POST firmato a quell'indirizzo quando il testo è pronto o la trascrizione non riesce.

Puoi configurare i webhook solo nell'API REST. Il server MCP non accetta un parametro per attivarli.

Scegliere l'URL che riceve le notifiche

In POST /v1/transcriptions, aggiungi webhook_url:

  • Nel body JSON, se passi un link video supportato o apri un caricamento in parti.
  • Nei campi del form, se carichi il file intero in una sola chiamata.

Controlla che l'URL:

  • Usi HTTPS.
  • Non includa nome utente o password.
  • Risolva a un indirizzo pubblico.

Con indirizzi privati, di loopback, link-local o comunque non pubblici, ricevi 422 invalid_request. Verifichiamo l'indirizzo sia alla creazione della trascrizione sia a ogni invio del webhook.

Non puoi usare webhook_url insieme a reuse_recent=true.

Quali eventi ricevi

typeQuando lo inviamo
transcription.completedLo stato passa a completed.
transcription.failedLo stato passa a failed. Trovi code e message in data.error.

Se annulli una trascrizione, non inviamo webhook.

Cosa contiene la notifica

Nel body JSON trovi:

  • created_at, la data di creazione dell'evento.
  • data, l'oggetto trascrizione senza il campo transcript.

Per ottenere il testo, usa l'id presente in data e chiama GET /v1/transcriptions/:id.

La chiamata ha questi header:

  • Content-Type: application/json
  • User-Agent: transcript1-webhooks/1.0
  • X-Transcript1-Signature, per verificare la firma come spiegato qui sotto.

Controllare la firma prima di usare i dati

Leggi l'header X-Transcript1-Signature, presente a ogni invio nel formato t=<unix seconds>,v1=<hex digest>.

  • t indica il momento della firma, come timestamp Unix in secondi.
  • Per ottenere v1, calcola l'HMAC-SHA256 di "<t>.<raw body>" e codificalo in esadecimale. Unisci il valore di t, un punto e il body esattamente come lo hai ricevuto. Come chiave usa tutto il segreto di firma, incluso whsec_.

Controlla la firma sui byte originali del body, prima del parsing JSON. Se fai il parsing e poi serializzi di nuovo, potresti cambiare spazi o ordine delle chiavi: la firma non corrisponderebbe più.

  • Per confrontare le firme, usa un confronto in tempo costante.
  • Scarta le chiamate con un t troppo distante dall'ora corrente. Negli esempi la tolleranza è di 5 minuti.
  • In Express, acquisisci il body come testo prima di fare il parsing.
  • In Flask, usa request.get_data(as_text=True) per fornire il valore di raw_body.

Gestire il segreto di firma

Ogni team ha un segreto con prefisso whsec_, usato per firmare i webhook di tutte le sue trascrizioni.

Puoi visualizzarlo o sostituirlo in “Webhooks”.

La sostituzione è immediata: da quel momento firmiamo i webhook con il nuovo segreto.

Risposte, nuovi tentativi e duplicati

  • Rispondi con un codice 2xx per confermare la ricezione.
  • Consideriamo il tentativo non riuscito se riceviamo un altro codice, incontriamo un errore di connessione o non otteniamo risposta entro 10 secondi.
  • In quel caso riproviamo, fino a 6 tentativi totali, con backoff esponenziale: aspettiamo 2, 4, 8, 16 e 32 minuti tra un invio e l'altro. L'ultimo arriva circa un'ora dopo il primo.
  • Indica direttamente l'URL finale: transcript1 non segue i redirect e considera una risposta 3xx un tentativo non riuscito.
  • Lo stesso evento può arrivare più volte, ad esempio se il tuo server risponde dopo il timeout. Riconosci i duplicati dalla coppia data.id e type e ignorali.