Verwenden Sie die REST API
Erstellen Sie einen API-Schlüssel, laden Sie ein Video hoch, erstellen Sie ein Untertitelprojekt und laden Sie den Export herunter.
Erstellen Sie einen API-Schlüssel
Integrationen sind mit Max-Abonnements verfügbar. Öffnen Sie Einstellungen → Integrationen, wählen Sie API-Schlüssel erstellen, geben Sie Namen und Berechtigungen an.
Gewähren Sie nur benötigte Rechte. Lesen, Schreiben und Export sind vorausgewählt. Behalten Sie Leserechte für Fortschritt und Ergebnisse.
| Berechtigung | Scope | Verwendung |
| --- | --- | --- |
| Projekte lesen | projects:read | Konto, Stile, Vorlagen, Projekte, Vorgänge und Ergebnisse |
| Projekte erstellen und verwalten | projects:write | Uploads, Erstellung, Abbruch und Wiederholung |
| Projekte exportieren | exports:write | Export, automatischer Export und Exportwiederholung |
| Webhooks verwalten | webhooks:manage | Endpunkte und Zustellungen; optional, nicht vorausgewählt |
Schlüssel laufen standardmäßig nach 90 Tagen ab. Wählen Sie 1–365 Tage oder kein Ablaufdatum. Widerrufen Sie ungenutzte Schlüssel. Dies blockiert neue Anfragen, beendet aber keine bereits angenommenen Aufgaben.
Prüfen Sie die erste Anfrage
Die Basis-URL ist https://api.captionbolt.com. Senden Sie den Schlüssel im Header Authorization von Ihrem Server oder lokalen Skript. Nicht in Browsercode, URL oder AI-Gesprächen offenlegen. Sitzungscookies werden nicht akzeptiert.
Geben Sie den Schlüssel in Bash ohne Eintrag im Verlauf ein:
read -r -s -p 'CaptionBolt API key: ' CAPTIONBOLT_API_KEY
export CAPTIONBOLT_API_KEY
printf '\n'
curl --fail-with-body https://api.captionbolt.com/v1/account \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Die Antwort enthält Guthaben, Uploadlimits, Ausgabeformate und Zugriffszeitraum. Guthabenwerte sind Sekunden; durch 60 teilen ergibt Minuten. Prüfen Sie credits.availableCreditsSec vor dem Start.
Stile finden Sie mit GET /v1/styles, gespeicherte Vorlagen mit GET /v1/presets. Listen akzeptieren search, limit (1–100, Standard 50) und offset.
Laden Sie Ihr Video hoch
Laden Sie vor der Projekterstellung MP4-, MOV-, WebM- oder AVI-Bytes hoch. Video-URL werden nicht angenommen:
- Erstellen Sie mit
POST /v1/uploads, echten Metadaten undIdempotency-Keyeinen Upload. - Prüfen Sie den Vorgang. Bei
completedist der endgültigeresourceIddie Upload-ID. Beifailedstoppen. - Lesen Sie
GET /v1/uploads/{uploadId}für Größe, Anzahl und vorhandene Teile. - Fordern Sie URL mit
POST /v1/uploads/{uploadId}/partsund{"partNumbers":[1]}an; höchstens 32 Teile pro Anfrage. - Übertragen Sie fehlende Teile per PUT mit exakter Bytelänge. Keinen API-Schlüssel an diese URL senden.
- Senden Sie
{}anPOST /v1/uploads/{uploadId}/completeund bestätigen Sieuploaded.
Geben Sie die gemessene Videodauer als durationSec an. Ohne sie wird die maximale Plandauer reserviert; wenig Guthaben kann dann auch kurze Videos blockieren. Die tatsächliche Dauer wird vor der Verarbeitung geprüft.
Beispiel für Upload und Projekterstellung
Dieses Node.js-24-Beispiel lädt in Teilen hoch und erstellt ein review-Projekt. Speichern Sie es als upload-and-create.mjs. Es liest Teile statt das gesamte Video in den Speicher zu laden. Ändern Sie die Datei währenddessen nicht.
import { createHash } from "node:crypto";
import { openAsBlob } from "node:fs";
import { stat } from "node:fs/promises";
import { basename, extname } from "node:path";
import { setTimeout as sleep } from "node:timers/promises";
const apiKey = process.env.CAPTIONBOLT_API_KEY;
const uploadKey = process.env.CAPTIONBOLT_UPLOAD_KEY;
const createKey = process.env.CAPTIONBOLT_CREATE_KEY;
const [filePath, durationText] = process.argv.slice(2);
const durationSec = Number(durationText);
if (!apiKey || !uploadKey || !createKey || !filePath || !Number.isFinite(durationSec) || durationSec <= 0) {
throw new Error("Set the API key and action keys, then supply a file and measured duration in seconds.");
}
async function api(method, path, input, idempotencyKey) {
const response = await fetch(`https://api.captionbolt.com${path}`, {
method,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
},
...(input === undefined ? {} : { body: JSON.stringify(input) }),
redirect: "error",
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
const error = await response.json().catch(() => null);
const detail = error?.message ?? error?.code ?? "Request failed";
throw new Error(`API HTTP ${response.status}: ${detail}; Retry-After: ${response.headers.get("retry-after") ?? "none"}`);
}
return response.json();
}
async function waitForOperation(operation) {
console.log("Operation ID:", operation.id);
const deadline = Date.now() + 120000;
while (operation.state !== "completed") {
if (operation.state === "failed") throw new Error(operation.error ?? "Action failed");
if (Date.now() >= deadline) throw new Error(`Continue polling operation ${operation.id}; do not start a new action.`);
await sleep(Math.max(1, operation.pollAfterSeconds) * 1000);
operation = await api("GET", `/v1/operations/${operation.id}`);
}
return operation;
}
const contentType = {
".mp4": "video/mp4", ".mov": "video/quicktime",
".webm": "video/webm", ".avi": "video/x-msvideo",
}[extname(filePath).toLowerCase()];
if (!contentType) throw new Error("Choose an MP4, MOV, WebM or AVI file.");
const metadata = await stat(filePath);
if (!metadata.isFile()) throw new Error("Choose a regular video file.");
const video = await openAsBlob(filePath, { type: contentType });
const fileName = basename(filePath);
const lastModifiedMs = Math.trunc(metadata.mtimeMs);
const fingerprint = createHash("sha256")
.update(`captionbolt-upload-v1\0${fileName}\0${video.size}\0${lastModifiedMs}\0${contentType}`)
.update(Buffer.from(await video.slice(0, 65536).arrayBuffer()))
.update(Buffer.from(await video.slice(Math.max(65536, video.size - 65536)).arrayBuffer()))
.digest("hex");
const uploaded = await waitForOperation(await api("POST", "/v1/uploads", {
fileName, fileSize: video.size, lastModifiedMs, contentType, fingerprint, durationSec,
}, uploadKey));
const uploadId = uploaded.resourceId;
console.log("Upload ID:", uploadId);
let upload = await api("GET", `/v1/uploads/${uploadId}`);
if (upload.status === "uploading" || upload.status === "completing") {
upload = await api("POST", `/v1/uploads/${uploadId}/resume`, {});
}
if (upload.status === "completing") {
throw new Error("Upload completion is still in progress. Wait, then rerun with the same action keys.");
}
if (upload.status === "expired" || upload.status === "aborted") {
throw new Error(`Upload is ${upload.status}. Start a new upload with new action keys.`);
}
if (upload.status !== "uploaded" && upload.status !== "consumed") {
const completed = new Set(upload.completedPartNumbers);
for (let number = 1; number <= upload.partCount; number++) {
if (completed.has(number)) continue;
const signed = await api("POST", `/v1/uploads/${uploadId}/parts`, { partNumbers: [number] });
for (const part of signed.signedParts) {
const start = (part.partNumber - 1) * upload.partSize;
const bytes = video.slice(start, start + part.contentLength);
if (bytes.size !== part.contentLength) throw new Error("File part length changed.");
const response = await fetch(part.url, {
method: "PUT", body: bytes,
headers: { "Content-Length": String(part.contentLength) },
redirect: "error", signal: AbortSignal.timeout(120000),
});
if (!response.ok) throw new Error(`Part ${part.partNumber}: HTTP ${response.status}`);
}
}
const finished = await api("POST", `/v1/uploads/${uploadId}/complete`, {});
if (finished.status !== "uploaded") throw new Error("Upload is not ready for project creation.");
}
const created = await waitForOperation(await api("POST", "/v1/projects", {
uploadSessionId: uploadId, mode: "review",
}, createKey));
console.log("Project ID:", created.resourceId);
Erzeugen Sie die Aktionsschlüssel für Upload und Erstellung einmal. Verwenden Sie sie für dieselben Anfragen erneut; andere Videos oder Aktionen benötigen neue Schlüssel.
export CAPTIONBOLT_UPLOAD_KEY="$(node -e 'console.log(crypto.randomUUID())')"
export CAPTIONBOLT_CREATE_KEY="$(node -e 'console.log(crypto.randomUUID())')"
node upload-and-create.mjs /absolute/path/lesson.mp4 60
Ersetzen Sie Pfad und 60 durch Datei und gemessene Sekunden. Verwenden Sie etwa FFprobe; nicht aus der Dateigröße schätzen. Das Skript zeigt Vorgangs-IDs zur Wiederaufnahme sowie Upload- und Projekt-ID.
Der SHA-256-Fingerabdruck enthält das gezeigte UTF-8-Metadatenpräfix sowie erste und letzte 64 KiB ohne doppelte Überlappung. Name, Größe, Änderungszeit und MIME-Typ müssen beim Fortsetzen gleich bleiben.
Verfolgen Sie Ihr Projekt
Setzen Sie PROJECT_ID auf die ausgegebene ID:
export PROJECT_ID='<project-id>'
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Lesen Sie status und beachten Sie pollAfterSeconds:
| Projektstatus | Nächster Schritt |
| --- | --- |
| processing | Videovorbereitung und Untertitel abwarten |
| ready | In CaptionBolt prüfen und speichern, dann exportieren |
| exporting | Export abwarten |
| completed | Ergebnis abrufen und Dateien herunterladen |
| failed | error lesen, Ursache beheben und ausdrücklich wiederholen |
| cancelled | Abfragen stoppen; dieser Durchlauf wurde abgebrochen |
Ein Vorgang completed bedeutet kein fertiges Video. Vorgänge beschreiben Aktionen; der Projektstatus beschreibt das Video. Prüfen Sie GET /v1/operations/{id} und stoppen Sie bei failed, um error zu lesen.
Bei Erstellung können Sie language und styleId oder presetId aus den Listen angeben. Ohne beide IDs gilt der Standardstil. mode: "auto-export" erzeugt nach Verarbeitung MP4 und benötigt zusätzlich exports:write. Bleibt das Projekt bei ready, prüfen Sie seinen autoExport-Vorgang.
Exportieren Sie gespeicherte Änderungen
Prüfen und speichern Sie review-Projekte im CaptionBolt-Editor. Erzeugen Sie den Exportschlüssel einmal und senden Sie:
export CAPTIONBOLT_EXPORT_KEY="$(node -e 'console.log(crypto.randomUUID())')"
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID/export" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $CAPTIONBOLT_EXPORT_KEY" \
--data '{"outputFormats":["mp4"]}'
Prüfen Sie zuerst den Vorgang, dann das Projekt bis completed. Bei Fehlern stoppen. Nur zuletzt gespeicherte Änderungen werden exportiert. outputFormats unterstützt mp4, srt, vtt, ass und txt gemäß aktuellem Plan. Standard ist MP4.
Laden Sie Dateien herunter
Sobald das Projekt completed ist:
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID/result" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Die Antwort enthält files mit jeweils format und url sowie expiresAt für Links. Laden Sie über die Datei-URL ohne API-Schlüssel herunter.
Links gelten höchstens zehn Minuten. Erneuern Sie sie innerhalb der 14 Tage Projektzugriff ab Erstellung. Erneuerung verlängert den Zugriff nicht. Projekt-expiresAt bezeichnet den Zugriffsschluss, Ergebnis-expiresAt den Linkablauf.
Fertige Projekte behalten ihren Export. Erstellen Sie zum erneuten Bearbeiten eine bearbeitbare Kopie in CaptionBolt.
Beheben Sie Fehler
| Antwort | Nächster Schritt |
| --- | --- |
| 400 | Felder und erforderliche Idempotenzheader prüfen |
| 401 | Gültigkeit, Ablauf und Widerruf des Schlüssels prüfen |
| 403 | Rechte und Limits prüfen; Schreibrechte erlauben kein Lesen des Fortschritts |
| 404 | ID und Konto prüfen; gelöschte oder abgelaufene Projekte sind nicht verfügbar |
| 409 | Status oder Wiederverwendung eines Schlüssels mit anderem Inhalt prüfen |
| 429 | Retry-After beachten und Fehler lesen; Anfragefrequenz oder Upload-/Aufgabenkapazität kann begrenzt sein. REST und MCP teilen 120 Anfragen pro Konto pro Minute |
| 503 oder Timeout | Warten, bekannten Vorgang abfragen oder identische Aktion mit ursprünglichem Schlüssel wiederholen |
HTTP-Erfolg kann eine später scheiternde Aktion annehmen. Prüfen Sie operation.state und operation.error. PAYMENT_REQUIRED bedeutet zu wenig verfügbare Minuten, eventuell durch aktive Aufgaben reserviert. PAYLOAD_TOO_LARGE bedeutet Dateigröße über dem aktuellen Limit. Prüfen Sie GET /v1/account, beheben Sie die Ursache und starten Sie erst nach bestätigtem Fehlschlag eine neue Aktion mit neuem Schlüssel. Der alte liefert denselben fehlgeschlagenen Vorgang.
Upload- und Projekterstellung, Export, Wiederholung und Abbruch benötigen Idempotency-Key: 1–128 druckbare ASCII-Zeichen ohne Leerzeichen. Neue Aktion, neuer Schlüssel. Bei Timeout Schlüssel und Inhalt beibehalten; eine verlorene Antwort beweist keinen Fehler.
Vergleichen Sie zum Fortsetzen Metadaten und Fingerabdruck mit GET /v1/uploads/{uploadId}, dann rufen Sie POST /v1/uploads/{uploadId}/resume auf. Nutzen Sie zurückgegebenen Status und Teile. Bei completing warten; bei uploading fehlende Teile senden und abschließen. expired oder aborted erfordert einen neuen Upload. DELETE /v1/uploads/{uploadId} beendet einen nicht konsumierten Upload, kein erstelltes Projekt.
Senden Sie für ausdrückliche Aktionen ein leeres JSON-Objekt an POST /v1/projects/{projectId}/retry oder /cancel, mit eigenem Schlüssel. Exportwiederholung erfordert zusätzlich exports:write.
GET /health/integrations benötigt keinen Schlüssel. 200 bedeutet bereit, 503 nicht verfügbar. Der Endpunkt prüft weder Ihre Zugangsdaten noch den Videoabschluss.
Vollständige Schemas: API-Referenz (öffnet sich in einem neuen Fenster) und OpenAPI. Webhooks melden Fortschritte ohne regelmäßige Abfragen.