Riferimento API
Ogni chiamata qui sotto ha un esempio di richiesta e di risposta; seguono gli stati dei processi, i codici di errore, i limiti e i tempi di conservazione. Sei nuovo? Inizia dalla guida per sviluppatori.
Panoramica
- URL di base:
https://api.video-to-notes.com. Ogni percorso inizia con/api/v1. - Richieste e risposte sono in JSON su HTTPS. Invia
Content-Type: application/jsoncon ogni corpo della richiesta. - Gli orari sono timestamp Unix in secondi (UTC). Le posizioni in una registrazione sono secondi dall’inizio del file.
- ID e cursori sono stringhe opache. Conservali esattamente come li ricevi.
- Ogni risposta di errore contiene un
request_id. Indicalo quando contatti l’assistenza. - Le risposte vengono inviate con
Cache-Control: no-store; non salvarle in cache condivise. - Il contratto leggibile dalle macchine si trova in openapi.json (OpenAPI 3.1) e paid-api.schema.json (JSON Schema).
Autenticazione
Invia la tua chiave API come token Bearer con ogni richiesta all’API. Le chiavi iniziano con vtn_prod_. Crei e revochi le chiavi dopo aver effettuato l’accesso al sito; una chiave API non può gestire chiavi, pagamenti o l’account.
Authorization: Bearer vtn_prod_...Una chiave mancante, malformata, scaduta o revocata restituisce 401 UNAUTHORIZED. Non inviare mai la chiave agli URL firmati dello storage: hanno una propria autorizzazione. Gli esempi curl di questa pagina usano questa piccola funzione, che passa la chiave a curl tramite lo standard input:
# Sends the key through stdin, so it never appears in the process list.
vtn() {
printf 'header = "Authorization: Bearer %s"\n' "$VTN_API_KEY" |
curl -s --config - "$@"
}Account
GET /api/v1/account
Restituisce il piano, i secondi rimanenti del piano, la fine dell’assegnazione attuale e le funzioni e i limiti che valgono per l’account in questo momento. Leggilo prima di caricare: se eligible o capabilities.api_admission_enabled è false, i nuovi processi vengono rifiutati.
vtn "$VTN_API_BASE/api/v1/account"{
"plan": "basic",
"eligible": true,
"remaining_seconds": 64800,
"allowance_expires_at": 1792886400,
"capabilities": {
"api_admission_enabled": true,
"key_creation_enabled": true,
"server_upload_enabled": true,
"long_jobs": true,
"formats": [
"mp4", "m4v", "mov", "webm", "mkv", "ogv", "avi", "wmv", "asf",
"flv", "mpg", "mpeg", "vob", "ts", "mts", "m2ts", "3gp", "3g2",
"mxf", "mp3", "m4a", "aac", "wav", "flac", "ogg", "oga", "opus",
"wma", "amr", "aiff", "aif", "caf", "ac3"
],
"audio_languages": [
"auto", "af", "am", "ar", "hy", "az", "be", "bn", "bs", "bg", "my",
"ca", "zh", "hr", "cs", "da", "nl", "en", "et", "tl", "fi", "fr",
"gl", "ka", "de", "el", "gu", "ha", "he", "hi", "hu", "is", "id",
"it", "ja", "kn", "kk", "km", "ko", "lv", "lt", "mk", "ms", "ml",
"mt", "mr", "mn", "ne", "no", "fa", "pl", "pt", "pa", "ro", "ru",
"sr", "sk", "sl", "es", "sw", "sv", "ta", "te", "th", "tr", "uk",
"uz", "vi"
],
"notes_languages": [
"auto", "af", "am", "ar", "hy", "az", "be", "bn", "bs", "bg", "my",
"ca", "zh", "hr", "cs", "da", "nl", "en", "et", "tl", "fi", "fr",
"gl", "ka", "de", "el", "gu", "ha", "he", "hi", "hu", "is", "id",
"it", "ja", "kn", "kk", "km", "ko", "lv", "lt", "mk", "ms", "ml",
"mt", "mr", "mn", "ne", "no", "fa", "pl", "pt", "pa", "ro", "ru",
"sr", "sk", "sl", "es", "sw", "sv", "ta", "te", "th", "tr", "uk",
"uz", "vi"
],
"download_formats": ["transcript_txt", "notes_md", "notes_html", "notes_pdf"],
"email_opt_in": false,
"limits": {
"max_upload_bytes": 8589934592,
"max_selection_seconds": null,
"active_jobs": 1,
"active_uploads": 1,
"daily_uploads": 10,
"daily_upload_bytes": 21474836480,
"new_requests_per_minute": 12,
"read_requests_per_minute": 60,
"poll_after_seconds": 5,
"result_page_bytes": 1000000,
"result_page_items": 32,
"upload_url_seconds": 900,
"download_url_seconds": 600,
"result_retention_seconds": 86400,
"recovery_retention_seconds": 172800,
"idempotency_retention_seconds": 34560000
}
}
}Caricamenti
I file vengono caricati in più parti direttamente nello storage tramite URL firmati. Un account può avere un solo caricamento attivo alla volta.
POST /api/v1/uploads
Avvia un caricamento. Invia la dimensione esatta del file in byte e la sua estensione. La risposta indica part_size e part_count: dividi il file in parti di part_size byte; l’ultima può essere più piccola.
vtn -X POST "$VTN_API_BASE/api/v1/uploads" \
-H "Content-Type: application/json" \
-d '{
"byte_size": 1048576,
"extension": "mp4"
}'{
"upload_id": "upload-1",
"state": "created",
"expires_at": 1792807200,
"part_size": 16777216,
"part_count": 1,
"error": null
}POST /api/v1/uploads/{upload_id}/parts
Restituisce URL di caricamento firmati per un numero di parti da 1 a 100. Ogni URL è valido fino a 15 minuti; richiedine uno nuovo quando scade o quando una parte va inviata di nuovo.
vtn -X POST "$VTN_API_BASE/api/v1/uploads/upload-1/parts" \
-H "Content-Type: application/json" \
-d '{
"part_numbers": [1]
}'{
"upload_id": "upload-1",
"targets": [
{
"part_number": 1,
"url": "https://example.invalid/signed",
"expires_at": 1792800900
}
]
}PUT {signed part URL}
Invia ogni parte come byte grezzi in un PUT HTTP al suo URL firmato, senza intestazione Authorization. Conserva l’intestazione di risposta ETag esattamente com’è, virgolette comprese. Ogni URL accetta solo la dimensione esatta della sua parte: part_size per ogni parte tranne l’ultima, che contiene i byte rimanenti. Un corpo di qualsiasi altra lunghezza viene rifiutato con HTTP 403.
# PART_URL comes from the parts response; send no Authorization header.
curl -s --upload-file part-0001.bin --dump-header - --output /dev/null \
"$PART_URL" | grep -i '^etag:'POST /api/v1/uploads/{upload_id}/complete
Completa il caricamento. Invia ogni numero di parte con il suo ETag. Completare un caricamento non avvia l’elaborazione; crea poi un processo.
vtn -X POST "$VTN_API_BASE/api/v1/uploads/upload-1/complete" \
-H "Content-Type: application/json" \
-d '{
"parts": [
{
"part_number": 1,
"etag": "opaque-etag"
}
]
}'{
"upload_id": "upload-1",
"state": "uploaded",
"expires_at": 1792807200,
"part_size": 16777216,
"part_count": 1,
"error": null
}GET /api/v1/uploads/{upload_id}
Restituisce lo stato di un caricamento: created, uploading, uploaded, probing, probed, failed, aborted o expired.
vtn "$VTN_API_BASE/api/v1/uploads/upload-1"{
"upload_id": "upload-1",
"state": "uploaded",
"expires_at": 1792807200,
"part_size": 16777216,
"part_count": 1,
"error": null
}POST /api/v1/uploads/{upload_id}/abort
Interrompe un caricamento che non userai e libera lo slot di caricamento dell’account. Chiamarlo di nuovo non crea problemi.
vtn -X POST "$VTN_API_BASE/api/v1/uploads/upload-1/abort" \
-H "Content-Type: application/json" \
-d '{}'Processi
Un processo trasforma un file caricato in una trascrizione e negli appunti. Un account può eseguire un solo processo alla volta.
POST /api/v1/jobs
Avvia l’elaborazione di un file caricato. L’intestazione Idempotency-Key è obbligatoria: da 1 a 128 caratteri ASCII stampabili senza spazi, nuova per ogni processo. I campi facoltativi sono range (start_seconds e end_seconds, entrambi obbligatori se presente), audio_language e notes_language. Restituisce 202 con il processo nello stato preparing. La stessa chiave con lo stesso corpo restituisce lo stesso processo; la stessa chiave con un altro corpo restituisce 409.
vtn -X POST "$VTN_API_BASE/api/v1/jobs" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"upload_id": "upload-1",
"range": {
"start_seconds": 5,
"end_seconds": 60
},
"audio_language": null,
"notes_language": "en"
}'{
"job_id": "job-1",
"state": "preparing",
"stage": "probe",
"created_at": 1792800000,
"expires_at": 1792807200,
"result_expires_at": null,
"idempotency_expires_at": 1827360000,
"poll_after_seconds": 5,
"status_url": "/api/v1/jobs/job-1",
"result_url": "/api/v1/jobs/job-1/result",
"completed_units": 0,
"total_units": null,
"charged_seconds": 0,
"result_available": false,
"can_resume": false,
"warnings": [],
"error": null
}GET /api/v1/jobs/{job_id}
Restituisce lo stato, la fase attuale, l’avanzamento (completed_units su total_units), charged_seconds, gli avvisi e l’eventuale errore. Anche un processo non riuscito restituisce 200; leggi il suo error.
vtn "$VTN_API_BASE/api/v1/jobs/job-1"{
"job_id": "job-1",
"state": "completed",
"stage": "terminal",
"created_at": 1792800000,
"expires_at": 1792807200,
"result_expires_at": 1792886400,
"idempotency_expires_at": 1827360000,
"poll_after_seconds": 5,
"status_url": "/api/v1/jobs/job-1",
"result_url": "/api/v1/jobs/job-1/result",
"completed_units": 8,
"total_units": 8,
"charged_seconds": 1140,
"result_available": true,
"can_resume": false,
"warnings": [],
"error": null
}POST /api/v1/jobs/{job_id}/cancel
Ferma un processo. Le parti completate restano disponibili e il tempo riservato non usato viene restituito. Chiamarlo di nuovo non crea problemi.
vtn -X POST "$VTN_API_BASE/api/v1/jobs/job-1/cancel" \
-H "Content-Type: application/json" \
-d '{}'POST /api/v1/jobs/{job_id}/resume
Riprende un processo interrotto quando il suo stato indica can_resume: true. Richiede un piano attivo e ricontrolla il tempo rimanente. Le parti già elaborate non vengono addebitate di nuovo.
vtn -X POST "$VTN_API_BASE/api/v1/jobs/job-1/resume" \
-H "Content-Type: application/json" \
-d '{}'Risultati
GET /api/v1/jobs/{job_id}/result
Restituisce una pagina di risultati con al massimo 32 elementi e 1 MB. Gli elementi sono ordinati e hanno un kind: transcript (testo con segmenti temporizzati), notes (Markdown), gap (audio che non è stato possibile elaborare) o warning. Passa next_cursor nel parametro di query cursor per ottenere la pagina successiva, finché è null. Ogni pagina può elencare fino a quattro link di download in artifacts, validi fino a 10 minuti. Se il processo viene ripreso mentre leggi, l’API restituisce 409 JOB_CONFLICT; ricomincia dalla prima pagina.
vtn --get "$VTN_API_BASE/api/v1/jobs/job-1/result" \
--data-urlencode "cursor=$NEXT_CURSOR" # omit for the first page{
"job_id": "job-1",
"snapshot": "generation-1",
"items": [
{
"index": 0,
"kind": "transcript",
"text": "Welcome to the weekly planning meeting.",
"segments": [
{
"text": "Welcome to the weekly planning meeting.",
"start_seconds": 0,
"end_seconds": 4.2,
"timing_quality": "approximate",
"timing_method": "provider_segment"
}
]
},
{
"index": 0,
"kind": "notes",
"text": "## Summary\n\nThe team agreed on the launch plan.\n\n## Key points\n\n- Launch moves to Monday."
}
],
"next_cursor": "opaque-account-bound-cursor",
"artifacts": [
{
"format": "notes_md",
"url": "https://example.invalid/signed-notes",
"expires_at": 1792800600
}
],
"charged_seconds": 1140,
"partial": false,
"expires_at": 1792886400
}Ogni segmento della trascrizione indica la propria timing_quality: aligned, approximate o unavailable. I tempi approssimativi sono vicini al parlato, ma non precisi alla parola.
I formati di download sono transcript_txt (TXT), notes_md (Markdown), notes_html (HTML) e notes_pdf (PDF). Gli appunti lunghi possono essere suddivisi in più file; scaricali in ordine. La chiamata dell’account elenca i formati disponibili in questo momento.
Stati di un processo
| Stato | Significato |
|---|---|
preparing | Il server sta controllando il file e pianificando il lavoro. |
queued | Il processo è in attesa di partire. |
processing | L’audio viene estratto, trascritto e trasformato in appunti. |
completed | Completato. La trascrizione completa e gli appunti sono disponibili. |
completed_with_gaps | Completato, ma una parte dell’audio non è stata elaborata. Vedi gli elementi gap. |
interrupted | Fermato prima della fine. Controlla can_resume. |
cancelled | Fermato su tua richiesta. Le parti completate restano disponibili. |
failed | Il processo non è stato elaborato. Vedi error. |
expired | Il contenuto non viene più conservato. |
stage indica il passaggio in corso: probe, planning, admission, extraction, transcription, notes, publication e infine terminal.
Errori
Ogni errore ha lo stesso corpo JSON. retryable indica se la stessa richiesta può riuscire più tardi, e retry_after_seconds quanto aspettare.
{
"error": {
"code": "RATE_LIMITED",
"message": "Request refused.",
"retryable": true,
"retry_after_seconds": 5
},
"request_id": "request-1"
}| Codice | HTTP | Significato | Cosa fare |
|---|---|---|---|
UNAUTHORIZED | 401 | La chiave manca, è malformata, scaduta, revocata o di un altro ambiente. | Controlla la chiave o creane una nuova. |
FORBIDDEN | 403 | La chiave non può essere usata per questa richiesta. | Gestisci chiavi e pagamenti sul sito. |
NOT_FOUND | 404 | Il caricamento o il processo non esiste o appartiene a un altro account. | Controlla l’ID. |
VALIDATION_ERROR | 400 | Il corpo, un’intestazione o un parametro di query non è valido. | Correggi la richiesta; non ripeterla invariata. |
PLAN_REQUIRED | 403 | L’account non ha un piano Basic o Plus attivo. | Scegli un piano e riprova. |
INSUFFICIENT_BALANCE | 403 | Il tempo rimanente del piano non copre l’audio selezionato. | Seleziona un intervallo più breve o aspetta il rinnovo delle ore. |
CAPABILITY_DISABLED | 503 | I nuovi processi sono in pausa o una funzione è disattivata. | Riprova più tardi; la chiamata dell’account mostra la situazione attuale. |
UNSUPPORTED_MEDIA | 400 | Il formato non è supportato, oppure la dimensione caricata è diversa da quella dichiarata. | Carica un file supportato con la sua dimensione esatta. |
MEDIA_TOO_LARGE | 413 | Il file supera il limite. | Carica un file di al massimo 8 GiB. |
PROBE_FAILED | 400 | Il file non ha una traccia audio decodificabile, oppure l’intervallo è fuori dal file. | Controlla il file e l’intervallo. Non viene addebitato tempo. |
IDEMPOTENCY_CONFLICT | 409 | La Idempotency-Key è già stata usata con altri dati. | Usa una nuova chiave per un nuovo processo. |
IDEMPOTENCY_EXPIRED | 410 | Il processo originale di questa chiave è scaduto. | Non ripeterlo; avvia un nuovo processo con una nuova chiave. |
JOB_CONFLICT | 409 | Un altro caricamento o processo è attivo, lo stato non consente questa azione oppure il risultato è cambiato durante la paginazione. | Aspetta il processo in corso o ricomincia la paginazione dalla prima pagina. |
RESULT_EXPIRED | 410 | Il risultato non viene più conservato. | Salva i risultati prima che scadano. |
RATE_LIMITED | 429 | Troppe richieste, oppure è stato raggiunto il limite giornaliero di caricamenti. | Aspetta quanto indicato da Retry-After, poi riprova. |
UNAVAILABLE | 503 | Un servizio è temporaneamente non disponibile. | Riprova con attese crescenti. |
Limiti
Questi sono i valori predefiniti. La chiamata dell’account restituisce i valori che valgono per il tuo account; fai riferimento a quelli.
| Limite | Predefinito | Campo in capabilities.limits |
|---|---|---|
| Dimensione del file | 8 GiB | max_upload_bytes |
| Audio selezionato per processo | Il tempo rimanente del tuo piano (null significa nessun altro tetto) | max_selection_seconds |
| Caricamenti e processi attivi | Uno di ciascuno per account | active_uploads, active_jobs |
| Caricamenti per giorno UTC | 10 caricamenti, 20 GiB in totale | daily_uploads, daily_upload_bytes |
| Nuovi processi e riprese | 12 al minuto | new_requests_per_minute |
| Altre richieste | 60 al minuto con tutte le chiavi | read_requests_per_minute |
| Controllo dello stato | Non più spesso di ogni 5 secondi | poll_after_seconds |
| Pagina di risultati | 32 elementi e 1 MB | result_page_items, result_page_bytes |
| URL di caricamento firmato | Valido fino a 15 minuti | upload_url_seconds |
| Link di download | Valido fino a 10 minuti | download_url_seconds |
| Autenticazioni non riuscite | 30 al minuto per indirizzo IP; dopo, ogni richiesta da quell’indirizzo riceve 429, anche con una chiave valida, finché non è passato il minuto | — |
Conservazione
| Dati | Conservati per |
|---|---|
| Caricamento senza processo | Circa 2 ore |
| Pagine di risultati e file da scaricare | Circa 24 ore dopo la fine del processo |
| Recupero della trascrizione | Circa 48 ore |
| Record di idempotenza, senza contenuti | 400 giorni |
Fanno fede i valori expires_at di ogni risposta; usali al posto di questi valori predefiniti.
Registro delle modifiche
La versione dell’API fa parte del percorso. Le modifiche alla v1 sono elencate qui con la loro data.
- 2026-10-24: API v1 disponibile per i piani Basic e Plus.