REST API’yi kullanın
API anahtarı oluşturun, video yükleyin, altyazı projesi oluşturup tamamlanan çıktıyı indirin.
API anahtarı oluşturun
Entegrasyonlar Max aboneliklerinde kullanılabilir. Ayarlar → Entegrasyonlar altında API anahtarı oluştur seçin, ad ve izinleri belirtin.
Yalnız gereken izinleri verin. Okuma, yazma ve dışa aktarma varsayılan seçilidir. İlerleme veya sonuçlar için okuma iznini koruyun.
| Ayarlardaki izin | Scope | Kullanım |
| --- | --- | --- |
| Projeleri oku | projects:read | Hesap, stiller, ön ayarlar, projeler, işlemler ve sonuçlar |
| Proje oluştur ve yönet | projects:write | Yükleme, oluşturma, iptal ve yeniden deneme |
| Projeleri dışa aktar | exports:write | Dışa aktarma, otomatik dışa aktarma ve yeniden denemeleri |
| Webhooks yönet | webhooks:manage | Uç noktalar ve teslimatlar; isteğe bağlı, varsayılan seçilmez |
Anahtarlar varsayılan 90 günde sona erer. 1–365 gün veya süresiz seçilebilir. Kullanılmayanları iptal edin. Yeni istekler engellenir, kabul edilmiş işler iptal edilmez.
İlk isteği doğrulayın
Temel URL https://api.captionbolt.com. Anahtarı sunucudan veya yerel betikten Authorization başlığında gönderin. Tarayıcı koduna, URL’ye veya AI konuşmalarına koymayın. Oturum çerezleri kabul edilmez.
Bash’te anahtarı geçmişe kaydetmeden girin:
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"
Yanıt bakiye, yükleme sınırları, çıktı biçimleri ve erişim süresini verir. Bakiye saniyedir; dakika için 60’a bölün. Başlamadan credits.availableCreditsSec kontrol edin.
Stiller için GET /v1/styles, kaydedilen ön ayarlar için GET /v1/presets kullanın. Listeler search, limit (1–100, varsayılan 50) ve offset kabul eder.
Videonuzu yükleyin
Proje oluşturmadan MP4, MOV, WebM veya AVI baytlarını yükleyin. Video URL’si kabul edilmez:
- Gerçek dosya metadatası ve
Idempotency-KeyilePOST /v1/uploadsçağırın. - İşlemi sorgulayın.
completedolduğunda sonresourceIdyükleme ID’sidir.faileddurumunda durun. - Parça boyutu, sayısı ve yüklenen parçalar için
GET /v1/uploads/{uploadId}okuyun. POST /v1/uploads/{uploadId}/partsve{"partNumbers":[1]}ile URL isteyin; en fazla 32 parça.- Eksik parçaları tam bayt uzunluğuyla URL’lerine PUT gönderin. Bu URL’lere API anahtarı göndermeyin.
POST /v1/uploads/{uploadId}/completeadresine{}gönderipuploadeddoğrulayın.
Ölçülen süreyi durationSec olarak verin. Vermezseniz planın en uzun video süresi ayrılır; düşük bakiye kısa videoyu bile engelleyebilir. İşlemeden önce gerçek süre kontrol edilir.
Yükleme ve proje oluşturma örneği
Node.js 24 örneği çok parçalı yükleme yapıp review projesi oluşturur. upload-and-create.mjs olarak kaydedin. Tüm videoyu belleğe almadan parça parça okur. Çalışırken dosyayı değiştirmeyin.
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);
Yükleme ve proje oluşturma için işlem anahtarlarını bir kez üretin. Aynı istekleri yinelemek için koruyun; farklı video veya işlemde yeni anahtar kullanın.
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
Yolu ve 60 değerini dosyanız ve ölçülen saniyeyle değiştirin. FFprobe gibi araç kullanın; dosya boyutundan tahmin etmeyin. Betik kurtarma için işlem ID’lerini, ardından yükleme ve proje ID’lerini gösterir.
SHA-256 parmak izi, gösterilen UTF-8 metadata önekini ve ilk/son 64 KiB’yi örtüşen baytları tekrarlamadan kullanır. Devam ederken ad, boyut, değiştirme zamanı ve MIME aynı kalmalıdır.
Projeyi izleyin
PROJECT_ID değerini gösterilen ID’ye ayarlayın:
export PROJECT_ID='<project-id>'
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
status okuyup pollAfterSeconds aralığını izleyin:
| Durum | Yapılacak işlem |
| --- | --- |
| processing | Video hazırlığı ve altyazıları bekleyin |
| ready | CaptionBolt’ta inceleyip kaydedin, sonra dışa aktarın |
| exporting | Dışa aktarmayı bekleyin |
| completed | Sonucu alıp dosyaları indirin |
| failed | error okuyun, nedeni düzeltip açıkça yeniden deneyin |
| cancelled | Sorgulamayı durdurun; bu çalışma iptal edildi |
İşlemin completed olması video bitti demek değildir. İşlem eylemi, proje durumu videoyu anlatır. GET /v1/operations/{id} sorgulayın; failed olursa durup error okuyun.
Oluştururken language ve listelerden styleId veya presetId verebilirsiniz. İkisini de vermemek varsayılan stili seçer. mode: "auto-export" işlem sonrası MP4 üretir ve ayrıca exports:write ister. ready durumunda kalırsa autoExport işlemini inceleyin.
Kaydedilen değişiklikleri dışa aktarın
review projesini CaptionBolt editöründe inceleyip kaydedin. Dışa aktarma anahtarını bir kez üretip isteği gönderin:
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"]}'
Önce işlemi, sonra projeyi completed olana dek sorgulayın; biri başarısızsa durun. Yalnız son kaydedilen değişiklikler alınır. outputFormats, plana göre mp4, srt, vtt, ass, txt içerebilir. Varsayılan MP4’tür.
Dosyaları indirin
Proje completed olduğunda:
curl --fail-with-body "https://api.captionbolt.com/v1/projects/$PROJECT_ID/result" \
-H "Authorization: Bearer $CAPTIONBOLT_API_KEY"
Yanıt files içinde format ve url, ayrıca linkler için expiresAt döndürür. Dosya URL’lerine anahtar göndermeden indirin.
Linkler en fazla on dakika geçerlidir. Oluşturulmasından itibaren 14 günlük proje erişimi içinde yenileyin; yenileme erişimi uzatmaz. Projenin expiresAt alanı erişim, sonucunki link sonudur.
Tamamlanan projeler mevcut çıktıyı korur. Yeniden düzenlemek için CaptionBolt’ta düzenlenebilir kopya oluşturun.
Hatalardan kurtulun
| Yanıt | Sonraki adım |
| --- | --- |
| 400 | Alanları ve gerekli idempotency başlıklarını kontrol edin |
| 401 | Anahtar geçerli, süresi dolmamış ve iptal edilmemiş olmalı |
| 403 | İzinleri ve sınırları kontrol edin; yazma, ilerlemeyi okumaya yetmez |
| 404 | ID ve hesabı kontrol edin; silinmiş veya süresi dolmuş projeler erişilemez |
| 409 | Durumu veya aynı anahtarın farklı veriyle kullanımını kontrol edin |
| 429 | Retry-After varsa bekleyip hatayı okuyun; istek sıklığı veya yükleme/iş kapasitesi sınırlı olabilir. REST ve MCP hesap başına dakikada 120 isteği paylaşır |
| 503 veya zaman aşımı | Bekleyin, bilinen işlemi sorgulayın veya aynı eylemi özgün anahtarla tekrarlayın |
HTTP başarısı sonradan başarısız olacak bir işlemi kabul edebilir. operation.state ve operation.error kontrol edin. PAYMENT_REQUIRED yetersiz kullanılabilir dakika demektir; aktif işler dakika ayırmış olabilir. PAYLOAD_TOO_LARGE dosyanın güncel sınırı aştığını belirtir. GET /v1/account kontrol edip düzeltin. Önceki işlem kesin başarısız olduktan sonra yeni anahtarla yeni işlem başlatın; eski anahtar aynı başarısız işlemi döndürür.
Yükleme ve proje oluşturma, dışa aktarma, yeniden deneme ve iptal için Idempotency-Key gerekir: boşluksuz 1–128 yazdırılabilir ASCII karakteri. Yeni işlem yeni anahtar ister. Zaman aşımında özgün anahtarı ve veriyi koruyun; yanıt kaybı başarısızlık kanıtı değildir.
Devam etmek için metadata ve parmak izini GET /v1/uploads/{uploadId} ile karşılaştırıp POST /v1/uploads/{uploadId}/resume çağırın. Dönen durum ve parça listesini kullanın. completing sürerse bekleyin; uploading iken eksikleri yükleyip tamamlayın. expired veya aborted yeni yükleme ister. DELETE /v1/uploads/{uploadId} tüketilmemiş yüklemeyi iptal eder, oluşturulmuş projeyi değil.
Açık yeniden deneme veya iptal için POST /v1/projects/{projectId}/retry ya da /cancel adresine boş JSON ve işlem anahtarını gönderin. Dışa aktarma yeniden denemesi ayrıca exports:write ister.
Anahtarsız GET /health/integrations, hazırsa 200, kullanılamıyorsa 503 verir. Kimlik bilgilerini veya video tamamlanmasını kontrol etmez.
Tüm şemalar API referansında (yeni bir pencerede açılır) ve OpenAPI’dedir. Düzenli sorgulama yerine aşama bildirimi için Webhooks kullanın.