Gebruik de REST API
Maak een API-sleutel, upload een video, maak een ondertitelproject en download de export.
Maak een API-sleutel
Integraties zijn beschikbaar bij Max-abonnementen. Open Instellingen → Integraties, kies API-sleutel maken, voer naam en rechten in.
Geef alleen benodigde rechten. Lezen, schrijven en exporteren zijn standaard geselecteerd. Behoud lezen voor voortgang en downloads.
| Recht in instellingen | Scope | Gebruik |
| --- | --- | --- |
| Projecten lezen | projects:read | Account, stijlen, presets, projecten, bewerkingen en resultaten |
| Projecten maken en beheren | projects:write | Uploads, aanmaken, annuleren en opnieuw proberen |
| Projecten exporteren | exports:write | Export, automatische export en export opnieuw proberen |
| Webhooks beheren | webhooks:manage | Endpoints en afleveringen; optioneel, standaard niet geselecteerd |
Sleutels verlopen standaard na 90 dagen. Kies 1–365 dagen of geen vervaldatum. Trek ongebruikte sleutels in. Dat blokkeert nieuwe verzoeken, zonder geaccepteerd werk te annuleren.
Controleer het eerste verzoek
De basis-URL is https://api.captionbolt.com. Stuur de sleutel in Authorization vanaf server of lokaal script. Zet hem niet in browsercode, URL of AI-gesprekken. Sessiecookies worden niet geaccepteerd.
Voer de sleutel in Bash in zonder deze in de geschiedenis op te nemen:
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"
Het antwoord geeft saldo, uploadlimieten, uitvoerformaten en toegangstermijn. Saldowaarden zijn seconden; deel door 60 voor minuten. Controleer credits.availableCreditsSec vooraf.
Stijlen: GET /v1/styles; opgeslagen presets: GET /v1/presets. Lijsten accepteren search, limit (1–100, standaard 50) en offset.
Upload uw video
Upload MP4-, MOV-, WebM- of AVI-bytes vóór aanmaken. Video-URL worden niet geaccepteerd:
- Maak een upload met
POST /v1/uploads, echte metadata enIdempotency-Key. - Volg de bewerking. Bij
completedis de definitieveresourceIdhet upload-ID. Stop bijfailed. - Lees
GET /v1/uploads/{uploadId}voor grootte, aantal en voltooide delen. - Vraag URL met
POST /v1/uploads/{uploadId}/partsen{"partNumbers":[1]}; maximaal 32 delen per keer. - Stuur ontbrekende delen via PUT met exacte lengte. Geen API-sleutel naar deze URL sturen.
- Stuur
{}naarPOST /v1/uploads/{uploadId}/completeen bevestiguploaded.
Geef de gemeten duur als durationSec. Zonder duur reserveert CaptionBolt de maximale planduur; weinig saldo kan daardoor ook korte video’s blokkeren. De echte duur wordt vooraf gecontroleerd.
Voorbeeld van upload en aanmaken
Dit Node.js-24-voorbeeld uploadt delen en maakt een review-project. Bewaar als upload-and-create.mjs. Het leest delen zonder de hele video in geheugen te laden. Wijzig het bestand niet tijdens uitvoering.
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);
Genereer de actiesleutels voor upload en aanmaken één keer. Bewaar ze voor dezelfde verzoeken; andere video’s of acties vereisen nieuwe sleutels.
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
Vervang pad en 60 door bestand en gemeten seconden. Gebruik bijvoorbeeld FFprobe; schat niet op bestandsgrootte. Het script toont bewerkings-ID’s voor herstel, daarna upload- en project-ID.
De SHA-256-vingerafdruk gebruikt het getoonde UTF-8-metadatavoorvoegsel plus eerste en laatste 64 KiB zonder dubbele overlap. Behoud naam, grootte, wijzigingstijd en MIME bij hervatten.
Volg uw project
Stel PROJECT_ID in op het getoonde ID:
export PROJECT_ID='<project-id>'
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Lees status en houd pollAfterSeconds aan:
| Status | Actie |
| --- | --- |
| processing | Wacht op voorbereiding en ondertitels |
| ready | Controleer en sla op in CaptionBolt, exporteer daarna |
| exporting | Wacht op export |
| completed | Haal resultaat op en download |
| failed | Lees error, herstel de oorzaak en probeer expliciet opnieuw |
| cancelled | Stop met opvragen; deze uitvoering is geannuleerd |
Bewerking completed betekent geen voltooide video. Bewerkingen beschrijven acties; projectstatus beschrijft video. Lees GET /v1/operations/{id}; stop bij failed en lees error.
Geef bij aanmaken eventueel language en styleId of presetId uit de lijsten. Zonder beide IDs geldt de standaardstijl. mode: "auto-export" maakt MP4 na verwerking en vereist ook exports:write. Bij blijvend ready, controleer de autoExport-bewerking.
Exporteer opgeslagen wijzigingen
Controleer en bewaar review-projecten in de CaptionBolt-editor. Genereer één exportsleutel en stuur:
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"]}'
Volg de bewerking en daarna het project tot completed; stop bij een fout. Alleen de laatste opgeslagen wijzigingen gaan mee. outputFormats accepteert mp4, srt, vtt, ass, txt volgens het huidige plan. MP4 is standaard.
Download uw bestanden
Wanneer het project completed is:
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID/result" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Het antwoord bevat files, elk met format en url, plus expiresAt voor links. Download via de bestands-URL zonder API-sleutel.
Links gelden maximaal tien minuten. Vernieuw binnen de 14 dagen projecttoegang vanaf aanmaken. Vernieuwen verlengt de toegang niet. Project-expiresAt is het toegangseinde; resultaat-expiresAt is het linkeinde.
Voltooide projecten behouden hun export. Maak een bewerkbare kopie in CaptionBolt voor nieuwe wijzigingen.
Herstel fouten
| Antwoord | Volgende stap |
| --- | --- |
| 400 | Controleer velden en vereiste idempotentieheaders |
| 401 | Controleer geldigheid, verloop en intrekking van de sleutel |
| 403 | Controleer rechten en limieten; schrijven geeft geen leesrecht |
| 404 | Controleer ID en account; verwijderde of verlopen projecten zijn onbeschikbaar |
| 409 | Controleer status of hergebruik van sleutel met andere invoer |
| 429 | Volg Retry-After en lees de fout; frequentie of upload/taakcapaciteit kan beperkt zijn. REST en MCP delen 120 verzoeken per account per minuut |
| 503 of timeout | Wacht, lees een bekende bewerking of herhaal identiek met de oorspronkelijke sleutel |
HTTP-succes kan een later mislukte actie accepteren. Lees operation.state en operation.error. PAYMENT_REQUIRED betekent onvoldoende beschikbare minuten, mogelijk gereserveerd door lopend werk. PAYLOAD_TOO_LARGE betekent bestand boven de huidige limiet. Controleer GET /v1/account, herstel en start pas na bevestigde fout een nieuwe actie met nieuwe sleutel. De oude retourneert dezelfde mislukte bewerking.
Upload- en projectaanmaak, export, opnieuw proberen en annuleren vereisen Idempotency-Key: 1–128 afdrukbare ASCII-tekens zonder spaties. Nieuwe actie, nieuwe sleutel. Behoud bij timeout de oude sleutel en invoer; een verloren antwoord bewijst geen fout.
Vergelijk voor hervatten metadata en vingerafdruk via GET /v1/uploads/{uploadId} en roep POST /v1/uploads/{uploadId}/resume aan. Gebruik teruggegeven status en delen. Wacht bij completing; upload ontbrekende delen en voltooi bij uploading. expired of aborted vereist een nieuwe upload. DELETE /v1/uploads/{uploadId} annuleert een niet-verbruikte upload, geen aangemaakt project.
Stuur voor expliciete acties een leeg JSON-object naar POST /v1/projects/{projectId}/retry of /cancel, met eigen sleutel. Export opnieuw proberen vereist ook exports:write.
GET /health/integrations zonder sleutel geeft 200 bij gereedheid en 503 bij onbeschikbaarheid. Het controleert geen toegangssleutels of videoafronding.
Volledige schema’s: API-referentie (opent in een nieuw venster) en OpenAPI. Gebruik Webhooks voor voortgangsmeldingen zonder polling.