API-Referenz
Jeder Aufruf unten hat eine Beispielanfrage und eine Beispielantwort, danach folgen Auftragszustände, Fehlercodes, Limits und Aufbewahrungsfristen. Neu hier? Beginne mit dem Entwicklerhandbuch.
Überblick
- Basis-URL:
https://api.video-to-notes.com. Jeder Pfad beginnt mit/api/v1. - Anfragen und Antworten sind JSON über HTTPS. Sende bei jedem Anfrage-Body
Content-Type: application/json. - Zeitangaben sind Unix-Zeitstempel in Sekunden (UTC). Positionen in einer Aufnahme sind Sekunden ab Dateibeginn.
- IDs und Cursor sind undurchsichtige Zeichenketten. Speichere sie genau so, wie du sie erhältst.
- Jede Fehlerantwort enthält eine
request_id. Gib sie an, wenn du den Support kontaktierst. - Antworten werden mit
Cache-Control: no-storegesendet; speichere sie nicht in gemeinsam genutzten Caches. - Der maschinenlesbare Vertrag steht in openapi.json (OpenAPI 3.1) und paid-api.schema.json (JSON Schema).
Authentifizierung
Sende deinen API-Schlüssel bei jeder API-Anfrage als Bearer-Token. Schlüssel beginnen mit vtn_prod_. Schlüssel erstellst und widerrufst du angemeldet auf der Website; ein API-Schlüssel kann keine Schlüssel, Zahlungen oder Kontodaten verwalten.
Authorization: Bearer vtn_prod_...Ein fehlender, fehlerhafter, abgelaufener oder widerrufener Schlüssel ergibt 401 UNAUTHORIZED. Sende den Schlüssel nie an die signierten Speicher-URLs: Sie tragen ihre eigene Autorisierung. Die curl-Beispiele auf dieser Seite nutzen diese kleine Hilfsfunktion, die den Schlüssel über die Standardeingabe an curl übergibt:
# 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 - "$@"
}Konto
GET /api/v1/account
Liefert den Tarif, die verbleibenden Tarifsekunden, das Ende des aktuellen Kontingents sowie die Funktionen und Limits, die gerade für das Konto gelten. Lies ihn vor dem Hochladen: Ist eligible oder capabilities.api_admission_enabled false, werden neue Aufträge abgelehnt.
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
}
}
}Uploads
Dateien werden in Teilen über signierte URLs direkt in den Speicher hochgeladen. Ein Konto kann jeweils einen aktiven Upload haben.
POST /api/v1/uploads
Startet einen Upload. Sende die genaue Dateigröße in Bytes und die Dateiendung. Die Antwort nennt part_size und part_count: Teile die Datei in Stücke von part_size Bytes; der letzte Teil darf kleiner sein.
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
Liefert signierte Upload-URLs für 1 bis 100 Teilnummern. Jede URL ist bis zu 15 Minuten gültig; fordere eine neue an, wenn sie abläuft oder ein Teil erneut gesendet werden muss.
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}
Sende jeden Teil als rohe Bytes eines HTTP-PUT an seine signierte URL, ohne Authorization-Header. Bewahre den Antwort-Header ETag genau so auf, wie er zurückkommt, einschließlich der Anführungszeichen. Jede URL akzeptiert nur die exakte Größe ihres Teils: part_size für jeden Teil außer dem letzten, der die restlichen Bytes enthält. Ein Body mit einer anderen Länge wird mit HTTP 403 abgelehnt.
# 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
Schließt den Upload ab. Sende jede Teilnummer mit ihrem ETag. Das Abschließen startet keine Verarbeitung; lege als Nächstes einen Auftrag an.
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}
Liefert den Zustand eines Uploads: created, uploading, uploaded, probing, probed, failed, aborted oder 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
Bricht einen Upload ab, den du nicht verwenden wirst, und gibt den Upload-Platz des Kontos frei. Ein erneuter Aufruf ist unbedenklich.
vtn -X POST "$VTN_API_BASE/api/v1/uploads/upload-1/abort" \
-H "Content-Type: application/json" \
-d '{}'Aufträge
Ein Auftrag verarbeitet eine hochgeladene Datei zu Transkript und Notizen. Ein Konto kann jeweils einen Auftrag ausführen.
POST /api/v1/jobs
Startet die Verarbeitung einer hochgeladenen Datei. Der Header Idempotency-Key ist Pflicht: 1 bis 128 druckbare ASCII-Zeichen ohne Leerzeichen, für jeden Auftrag neu. Optionale Felder sind range (start_seconds und end_seconds, beide nötig, wenn vorhanden), audio_language und notes_language. Liefert 202 mit dem Auftrag im Zustand preparing. Derselbe Schlüssel mit demselben Body liefert denselben Auftrag; derselbe Schlüssel mit anderem Body liefert 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}
Liefert Zustand, aktuelle Phase, Fortschritt (completed_units von total_units), charged_seconds, Warnungen und einen eventuellen Fehler. Auch ein gescheiterter Auftrag liefert 200; lies sein 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
Stoppt einen Auftrag. Fertige Teile bleiben verfügbar und nicht genutzte reservierte Zeit wird zurückgegeben. Ein erneuter Aufruf ist unbedenklich.
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
Setzt einen unterbrochenen Auftrag fort, wenn sein Status can_resume: true meldet. Das erfordert einen aktiven Tarif und prüft deine verbleibende Zeit erneut. Bereits verarbeitete Teile werden nicht noch einmal berechnet.
vtn -X POST "$VTN_API_BASE/api/v1/jobs/job-1/resume" \
-H "Content-Type: application/json" \
-d '{}'Ergebnisse
GET /api/v1/jobs/{job_id}/result
Liefert eine Ergebnisseite mit bis zu 32 Einträgen und 1 MB. Die Einträge sind geordnet und haben eine kind: transcript (Text mit zeitlich zugeordneten Segmenten), notes (Markdown), gap (Audio, das nicht verarbeitet werden konnte) oder warning. Übergib next_cursor als Query-Parameter cursor, um die nächste Seite zu erhalten, bis der Wert null ist. Jede Seite kann in artifacts bis zu vier Download-Links aufführen, gültig bis zu 10 Minuten. Wird der Auftrag fortgesetzt, während du liest, antwortet die API mit 409 JOB_CONFLICT; beginne dann wieder mit der ersten Seite.
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
}Jedes Transkriptsegment meldet seine timing_quality: aligned, approximate oder unavailable. Ungefähre Zeiten liegen nah an der Sprache, sind aber nicht wortgenau.
Die Download-Formate sind transcript_txt (TXT), notes_md (Markdown), notes_html (HTML) und notes_pdf (PDF). Lange Notizen können auf mehrere Dateien verteilt sein; lade sie in der Reihenfolge herunter. Der Kontoaufruf listet die derzeit verfügbaren Formate.
Auftragszustände
| Zustand | Bedeutung |
|---|---|
preparing | Der Server prüft die Datei und plant die Arbeit. |
queued | Der Auftrag wartet auf den Start. |
processing | Das Audio wird extrahiert, transkribiert und zu Notizen verarbeitet. |
completed | Fertig. Das vollständige Transkript und die Notizen sind verfügbar. |
completed_with_gaps | Fertig, aber ein Teil des Audios konnte nicht verarbeitet werden. Siehe die gap-Einträge. |
interrupted | Vor dem Ende angehalten. Prüfe can_resume. |
cancelled | Auf deinen Wunsch gestoppt. Fertige Teile bleiben verfügbar. |
failed | Der Auftrag konnte nicht verarbeitet werden. Siehe error. |
expired | Die Inhalte werden nicht mehr aufbewahrt. |
stage zeigt den laufenden Schritt: probe, planning, admission, extraction, transcription, notes, publication und zuletzt terminal.
Fehler
Jeder Fehler hat denselben JSON-Body. retryable sagt dir, ob dieselbe Anfrage später gelingen kann, und retry_after_seconds, wie lange du warten sollst.
{
"error": {
"code": "RATE_LIMITED",
"message": "Request refused.",
"retryable": true,
"retry_after_seconds": 5
},
"request_id": "request-1"
}| Code | HTTP | Bedeutung | Was tun |
|---|---|---|---|
UNAUTHORIZED | 401 | Der Schlüssel fehlt, ist fehlerhaft, abgelaufen, widerrufen oder für eine andere Umgebung. | Prüfe den Schlüssel oder erstelle einen neuen. |
FORBIDDEN | 403 | Der Schlüssel kann für diese Anfrage nicht verwendet werden. | Verwalte Schlüssel und Zahlungen auf der Website. |
NOT_FOUND | 404 | Der Upload oder Auftrag existiert nicht oder gehört zu einem anderen Konto. | Prüfe die ID. |
VALIDATION_ERROR | 400 | Der Body, ein Header oder ein Query-Parameter ist ungültig. | Korrigiere die Anfrage; wiederhole sie nicht unverändert. |
PLAN_REQUIRED | 403 | Das Konto hat keinen aktiven Basic- oder Plus-Tarif. | Wähle einen Tarif und versuche es dann erneut. |
INSUFFICIENT_BALANCE | 403 | Die verbleibende Tarifzeit reicht für das ausgewählte Audio nicht aus. | Wähle einen kürzeren Bereich oder warte, bis deine Stunden erneuert werden. |
CAPABILITY_DISABLED | 503 | Neue Aufträge sind pausiert oder eine Funktion ist ausgeschaltet. | Versuche es später erneut; der Kontoaufruf zeigt den aktuellen Stand. |
UNSUPPORTED_MEDIA | 400 | Das Format wird nicht unterstützt oder die hochgeladene Größe weicht von der angegebenen ab. | Lade eine unterstützte Datei mit ihrer genauen Größe hoch. |
MEDIA_TOO_LARGE | 413 | Die Datei ist größer als das Limit. | Lade eine Datei von höchstens 8 GiB hoch. |
PROBE_FAILED | 400 | Die Datei hat keine dekodierbare Audiospur oder der Bereich liegt außerhalb der Datei. | Prüfe Datei und Bereich. Es wird keine Zeit berechnet. |
IDEMPOTENCY_CONFLICT | 409 | Der Idempotency-Key wurde bereits mit anderen Eingaben verwendet. | Verwende für einen neuen Auftrag einen neuen Schlüssel. |
IDEMPOTENCY_EXPIRED | 410 | Der ursprüngliche Auftrag zu diesem Schlüssel ist abgelaufen. | Wiederhole ihn nicht; starte einen neuen Auftrag mit neuem Schlüssel. |
JOB_CONFLICT | 409 | Ein anderer Upload oder Auftrag ist aktiv, der Zustand erlaubt diese Aktion nicht oder das Ergebnis hat sich beim Blättern geändert. | Warte auf den aktuellen Auftrag oder beginne das Blättern wieder mit der ersten Seite. |
RESULT_EXPIRED | 410 | Das Ergebnis wird nicht mehr aufbewahrt. | Speichere Ergebnisse, bevor sie ablaufen. |
RATE_LIMITED | 429 | Zu viele Anfragen oder das tägliche Upload-Limit ist erreicht. | Warte auf Retry-After und versuche es dann erneut. |
UNAVAILABLE | 503 | Ein Dienst ist vorübergehend nicht verfügbar. | Wiederhole die Anfrage mit wachsenden Abständen. |
Limits
Dies sind die Standardwerte. Der Kontoaufruf liefert die Werte, die für dein Konto gelten; verlass dich auf diese.
| Limit | Standard | Feld in capabilities.limits |
|---|---|---|
| Dateigröße | 8 GiB | max_upload_bytes |
| Ausgewähltes Audio pro Auftrag | Deine verbleibende Tarifzeit (null bedeutet keine weitere Obergrenze) | max_selection_seconds |
| Aktive Uploads und Aufträge | Je einer pro Konto | active_uploads, active_jobs |
| Uploads pro UTC-Tag | 10 Uploads, zusammen 20 GiB | daily_uploads, daily_upload_bytes |
| Neue Aufträge und Fortsetzungen | 12 pro Minute | new_requests_per_minute |
| Weitere Anfragen | 60 pro Minute über alle Schlüssel | read_requests_per_minute |
| Statusabfragen | Nicht öfter als alle 5 Sekunden | poll_after_seconds |
| Ergebnisseite | 32 Einträge und 1 MB | result_page_items, result_page_bytes |
| Signierte Upload-URL | Bis zu 15 Minuten gültig | upload_url_seconds |
| Download-Link | Bis zu 10 Minuten gültig | download_url_seconds |
| Fehlgeschlagene Authentifizierung | 30 pro Minute pro IP-Adresse; danach erhält jede Anfrage von dieser Adresse 429, auch mit gültigem Schlüssel, bis die Minute vorbei ist | — |
Aufbewahrung
| Daten | Aufbewahrt für |
|---|---|
| Upload ohne Auftrag | Etwa 2 Stunden |
| Ergebnisseiten und Download-Dateien | Etwa 24 Stunden nach Abschluss des Auftrags |
| Transkript-Wiederherstellung | Etwa 48 Stunden |
| Idempotenz-Eintrag, ohne Inhalte | 400 Tage |
Maßgeblich sind die expires_at-Werte in jeder Antwort; nutze sie statt dieser Standardwerte.
Änderungsprotokoll
Die API-Version ist Teil des Pfads. Änderungen an v1 werden hier mit Datum aufgeführt.
- 2026-10-24: API v1 für die Tarife Basic und Plus verfügbar.