Referencia de la API
Cada llamada tiene un ejemplo de solicitud y de respuesta; después vienen los estados de las tareas, los códigos de error, los límites y los plazos de conservación. ¿Eres nuevo? Empieza por la guía para desarrolladores.
Resumen
- URL base:
https://api.video-to-notes.com. Todas las rutas empiezan por/api/v1. - Las solicitudes y las respuestas son JSON sobre HTTPS. Envía
Content-Type: application/jsoncon cada cuerpo de solicitud. - Las horas son marcas de tiempo Unix en segundos (UTC). Las posiciones en una grabación son segundos desde el inicio del archivo.
- Los ID y los cursores son cadenas opacas. Guárdalos exactamente como los recibes.
- Cada respuesta de error contiene un
request_id. Inclúyelo cuando contactes con soporte. - Las respuestas se envían con
Cache-Control: no-store; no las guardes en cachés compartidas. - El contrato legible por máquina está en openapi.json (OpenAPI 3.1) y paid-api.schema.json (JSON Schema).
Autenticación
Envía tu clave API como token Bearer en cada solicitud a la API. Las claves empiezan por vtn_prod_. Crea y revoca las claves con la sesión iniciada en la web; una clave API no puede gestionar claves, pagos ni la cuenta.
Authorization: Bearer vtn_prod_...Una clave ausente, mal formada, caducada o revocada devuelve 401 UNAUTHORIZED. Nunca envíes la clave a las URL firmadas del almacenamiento: llevan su propia autorización. Los ejemplos de curl de esta página usan esta pequeña función, que pasa la clave a curl por la entrada estándar:
# 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 - "$@"
}Cuenta
GET /api/v1/account
Devuelve el plan, los segundos restantes del plan, el final de la asignación actual y las funciones y límites que se aplican a la cuenta en este momento. Consúltalo antes de subir: si eligible o capabilities.api_admission_enabled es false, las tareas nuevas se rechazan.
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
}
}
}Subidas
Los archivos se suben por partes directamente al almacenamiento mediante URL firmadas. Una cuenta puede tener una sola subida activa a la vez.
POST /api/v1/uploads
Inicia una subida. Envía el tamaño exacto del archivo en bytes y su extensión. La respuesta indica part_size y part_count: divide el archivo en partes de part_size bytes; la última puede ser menor.
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
Devuelve URL de subida firmadas para entre 1 y 100 números de parte. Cada URL es válida hasta 15 minutos; pide una nueva cuando caduque o cuando haya que volver a enviar una parte.
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}
Envía cada parte como bytes sin procesar en un PUT HTTP a su URL firmada, sin cabecera Authorization. Guarda la cabecera de respuesta ETag exactamente como llega, comillas incluidas. Cada URL solo acepta el tamaño exacto de su parte: part_size para cada parte salvo la última, que lleva los bytes restantes. Un cuerpo de cualquier otra longitud se rechaza 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
Termina la subida. Envía cada número de parte con su ETag. Terminar una subida no inicia el procesamiento; después crea una tarea.
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}
Devuelve el estado de una subida: 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
Anula una subida que no vas a usar y libera el hueco de subida de la cuenta. Volver a llamarla no tiene riesgo.
vtn -X POST "$VTN_API_BASE/api/v1/uploads/upload-1/abort" \
-H "Content-Type: application/json" \
-d '{}'Tareas
Una tarea convierte un archivo subido en una transcripción y unos apuntes. Una cuenta puede ejecutar una sola tarea a la vez.
POST /api/v1/jobs
Inicia el procesamiento de un archivo subido. La cabecera Idempotency-Key es obligatoria: de 1 a 128 caracteres ASCII imprimibles sin espacios, nueva para cada tarea. Los campos opcionales son range (start_seconds y end_seconds, ambos obligatorios si se envía), audio_language y notes_language. Devuelve 202 con la tarea en estado preparing. La misma clave con el mismo cuerpo devuelve la misma tarea; la misma clave con otro cuerpo devuelve 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}
Devuelve el estado, la fase actual, el progreso (completed_units de total_units), charged_seconds, los avisos y el posible error. Una tarea fallida también devuelve 200; lee su 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
Detiene una tarea. Las partes terminadas siguen disponibles y el tiempo reservado que no se usó se devuelve. Volver a llamarla no tiene riesgo.
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
Reanuda una tarea interrumpida cuando su estado indica can_resume: true. Requiere un plan activo y vuelve a comprobar el tiempo que te queda. Las partes ya procesadas no se cobran otra vez.
vtn -X POST "$VTN_API_BASE/api/v1/jobs/job-1/resume" \
-H "Content-Type: application/json" \
-d '{}'Resultados
GET /api/v1/jobs/{job_id}/result
Devuelve una página de resultados con hasta 32 elementos y 1 MB. Los elementos están ordenados y tienen un kind: transcript (texto con segmentos con tiempos), notes (Markdown), gap (audio que no se pudo procesar) o warning. Pasa next_cursor en el parámetro de consulta cursor para obtener la página siguiente, hasta que sea null. Cada página puede incluir hasta cuatro enlaces de descarga en artifacts, válidos hasta 10 minutos. Si la tarea se reanuda mientras lees, la API devuelve 409 JOB_CONFLICT; vuelve a empezar por la primera página.
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
}Cada segmento de la transcripción indica su timing_quality: aligned, approximate o unavailable. Los tiempos aproximados están cerca del habla, pero no son exactos a nivel de palabra.
Los formatos de descarga son transcript_txt (TXT), notes_md (Markdown), notes_html (HTML) y notes_pdf (PDF). Los apuntes largos pueden repartirse en varios archivos; descárgalos en orden. La llamada de la cuenta muestra los formatos disponibles en este momento.
Estados de una tarea
| Estado | Significado |
|---|---|
preparing | El servidor está comprobando el archivo y planificando el trabajo. |
queued | La tarea está esperando para empezar. |
processing | El audio se está extrayendo, transcribiendo y convirtiendo en apuntes. |
completed | Terminada. La transcripción completa y los apuntes están disponibles. |
completed_with_gaps | Terminada, pero parte del audio no se pudo procesar. Consulta los elementos gap. |
interrupted | Detenida antes del final. Revisa can_resume. |
cancelled | Detenida a petición tuya. Las partes terminadas siguen disponibles. |
failed | La tarea no se pudo procesar. Consulta error. |
expired | El contenido ya no se conserva. |
stage indica el paso en curso: probe, planning, admission, extraction, transcription, notes, publication y, por último, terminal.
Errores
Todos los errores tienen el mismo cuerpo JSON. retryable indica si la misma solicitud puede funcionar más tarde, y retry_after_seconds, cuánto esperar.
{
"error": {
"code": "RATE_LIMITED",
"message": "Request refused.",
"retryable": true,
"retry_after_seconds": 5
},
"request_id": "request-1"
}| Código | HTTP | Significado | Qué hacer |
|---|---|---|---|
UNAUTHORIZED | 401 | La clave falta, está mal formada, ha caducado, se ha revocado o es de otro entorno. | Revisa la clave o crea una nueva. |
FORBIDDEN | 403 | La clave no se puede usar para esta solicitud. | Gestiona las claves y los pagos en la web. |
NOT_FOUND | 404 | La subida o la tarea no existe o pertenece a otra cuenta. | Revisa el ID. |
VALIDATION_ERROR | 400 | El cuerpo, una cabecera o un parámetro de consulta no es válido. | Corrige la solicitud; no la repitas sin cambios. |
PLAN_REQUIRED | 403 | La cuenta no tiene un plan Basic o Plus activo. | Elige un plan y vuelve a intentarlo. |
INSUFFICIENT_BALANCE | 403 | El tiempo restante del plan no cubre el audio seleccionado. | Selecciona un intervalo más corto o espera a que se renueven tus horas. |
CAPABILITY_DISABLED | 503 | Las tareas nuevas están en pausa o una función está desactivada. | Inténtalo más tarde; la llamada de la cuenta muestra la situación actual. |
UNSUPPORTED_MEDIA | 400 | El formato no es compatible, o el tamaño subido no coincide con el declarado. | Sube un archivo compatible con su tamaño exacto. |
MEDIA_TOO_LARGE | 413 | El archivo supera el límite. | Sube un archivo de 8 GiB como máximo. |
PROBE_FAILED | 400 | El archivo no tiene una pista de audio decodificable o el intervalo queda fuera del archivo. | Revisa el archivo y el intervalo. No se cobra tiempo. |
IDEMPOTENCY_CONFLICT | 409 | La Idempotency-Key ya se usó con otros datos. | Usa una clave nueva para una tarea nueva. |
IDEMPOTENCY_EXPIRED | 410 | La tarea original de esta clave ha caducado. | No la repitas; inicia una tarea nueva con una clave nueva. |
JOB_CONFLICT | 409 | Hay otra subida u otra tarea activa, el estado no permite esta acción o el resultado cambió mientras paginabas. | Espera a la tarea actual o vuelve a paginar desde la primera página. |
RESULT_EXPIRED | 410 | El resultado ya no se conserva. | Guarda los resultados antes de que caduquen. |
RATE_LIMITED | 429 | Demasiadas solicitudes, o se ha alcanzado el límite diario de subidas. | Espera lo que indique Retry-After y vuelve a intentarlo. |
UNAVAILABLE | 503 | Un servicio no está disponible temporalmente. | Reintenta con esperas cada vez mayores. |
Límites
Estos son los valores predeterminados. La llamada de la cuenta devuelve los valores que se aplican a tu cuenta; básate en ellos.
| Límite | Predeterminado | Campo en capabilities.limits |
|---|---|---|
| Tamaño de archivo | 8 GiB | max_upload_bytes |
| Audio seleccionado por tarea | El tiempo restante de tu plan (null significa sin otro tope) | max_selection_seconds |
| Subidas y tareas activas | Una de cada por cuenta | active_uploads, active_jobs |
| Subidas por día UTC | 10 subidas, 20 GiB en total | daily_uploads, daily_upload_bytes |
| Tareas nuevas y reanudaciones | 12 por minuto | new_requests_per_minute |
| Otras solicitudes | 60 por minuto con todas las claves | read_requests_per_minute |
| Consulta del estado | No más de una vez cada 5 segundos | poll_after_seconds |
| Página de resultados | 32 elementos y 1 MB | result_page_items, result_page_bytes |
| URL de subida firmada | Válida hasta 15 minutos | upload_url_seconds |
| Enlace de descarga | Válido hasta 10 minutos | download_url_seconds |
| Autenticaciones fallidas | 30 por minuto por dirección IP; después, cada solicitud desde esa dirección recibe 429, incluso con una clave válida, hasta que pase el minuto | — |
Conservación
| Datos | Se conservan |
|---|---|
| Subida sin tarea | Unas 2 horas |
| Páginas de resultados y archivos de descarga | Unas 24 horas después de terminar la tarea |
| Recuperación de la transcripción | Unas 48 horas |
| Registro de idempotencia, sin contenido | 400 días |
Los valores expires_at de cada respuesta son los que cuentan; úsalos en lugar de estos valores predeterminados.
Registro de cambios
La versión de la API forma parte de la ruta. Los cambios de v1 se recogen aquí con su fecha.
- 2026-10-24: API v1 disponible para los planes Basic y Plus.