TimedSubs API
API documentation for subtitle automation
Public docs for Studio and Agency private-beta integrations. Learn the auth model, upload flow, job status, export gates, usage, webhooks, and safety boundaries before creating a workspace key.
Public docs, private access
Reading this page does not grant API access. API keys, usage, webhook secrets, signed URLs, Try-it actions, project data, and all API calls remain authenticated, permissioned, and plan-gated.
What is public
Endpoint behavior, examples, auth headers, idempotency, errors, rate limits, webhook signing, and export readiness rules.
What needs sign-in
API key creation, usage records, webhook secrets, project data, workspace settings, and account-specific Try-it actions.
What stays closed
Internal admin routes, provider credentials, ops health, worker control endpoints, raw storage keys, and non-signed file access.
Required headers
Keep keys on your server. Do not put API keys in browser code, mobile apps, client logs, support tickets, screenshots, or public repositories.
Authorization: Bearer <TIMEDSUBS_API_KEY>
Content-Type: application/json
Idempotency-Key: <stable-unique-operation-key>Reusing an idempotency key with the same body can replay with `X-Idempotency-Replayed: true` and `idempotencyReplayed: true`. Upload-sign replays keep the same file and return a freshly signed upload URL; other endpoints replay the stored response. Reusing a key with a different body returns `409 idempotency_key_conflict`.
curl example
A minimal project creation request with authentication and idempotency.
curl -sS "$BASE_URL/api/v1/projects" \
-H "Authorization: Bearer $TIMEDSUBS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: project-client-reel-20260602-001" \
-d '{
"name": "Client reel subtitles",
"sourceLanguage": "en",
"targetLanguages": ["zh"],
"durationSeconds": 180
}'Recommended call order
POST /api/v1/webhooksPOST /api/v1/projectsPOST /api/v1/projects/{projectId}/uploads/signPUT {upload.uploadUrl}POST /api/v1/projects/{projectId}/uploads/confirmPOST /api/v1/projects/{projectId}/source-subtitlePOST /api/v1/projects/{projectId}/jobsGET /api/v1/jobs/{jobId}GET /api/v1/projects/{projectId}GET /api/v1/projects/{projectId}/exportsGET /api/v1/projects/{projectId}/exports/{format}GET /api/v1/usageInputs, outputs, and examples
POST
/api/v1/projects
Create a script + audio project, or a subtitle-file translation project with `mode: "translate"`.
Input
{ "name": "Client reel subtitles", "mode": "script_audio", "sourceLanguage": "en", "targetLanguages": ["zh"], "durationSeconds": 180 }Output
{ "project": { "id": "project-id", "mode": "script_audio", "sourceLanguage": "en" } }POST
/api/v1/projects/{projectId}/uploads/sign
Request a signed upload URL for audio, video, script, or subtitle assets.
Input
{ "kind": "audio", "filename": "voiceover.wav", "contentType": "audio/wav", "sizeBytes": 8234567 }Output
{ "upload": { "uploadUrl": "https://signed-upload-url", "objectKey": "workspaces/..." }, "file": { "id": "file-id", "uploaded": false } }POST
/api/v1/projects/{projectId}/source-subtitle
Parse a confirmed subtitle file into source cues for translation projects; required before a translate job.
Input
{ "filename": "source.srt", "contentType": "application/x-subrip", "text": "1\n00:00:00,000 --> 00:00:02,000\nHello world." }Output
{ "project": { "id": "project-id", "status": "ready", "sourceLanguage": "en" }, "source": { "cueCount": 1, "durationSeconds": 2 } }POST
/api/v1/projects/{projectId}/jobs
Accept or enqueue alignment/translation through the background worker path.
Input
{ "type": "align" }Output
{ "jobId": "job-id", "reused": false, "dispatchState": "enqueued", "enqueued": true }GET
/api/v1/jobs/{jobId}
Poll job status.
Input
No request body.Output
{ "job": { "id": "job-id", "status": "processing", "projectId": "project-id" } }GET
/api/v1/projects/{projectId}/exports
List source and translation exports, including signed download URLs when ready.
Input
No request body.Output
{ "exports": [{ "format": "srt", "status": "ready", "delivery": { "blocking": false }, "downloadUrl": "https://timedsubs.com/api/v1/..." }, { "format": "srt", "status": "blocked", "delivery": { "blocker": { "category": "timing_issue" } }, "downloadUrl": null }] }POST
/api/v1/webhooks
Register or update a public HTTPS callback.
Input
{ "url": "https://example.com/timedsubs/webhook", "events": ["job.status", "export.ready"], "enabled": true }Output
{ "webhook": { "id": "webhook-id" }, "signing": { "algorithm": "HMAC-SHA256" }, "signingSecret": "<shown-once>" }GET
/api/v1/usage
Read usage and entitlement state for the API key workspace.
Input
No request body.Output
{ "usage": { "alignmentMinutes": 120, "translationMinutes": 45 }, "entitlements": { "planId": "studio" }, "access": { "accessSource": "workspace_plan", "opsFullAccess": false } }Webhook signing
TimedSubs sends `timedsubs-webhook-signature` and `timedsubs-webhook-timestamp`. Verify `HMAC-SHA256` over `timestamp.body` using `SHA-256(signingSecret)` as the key.
Rate limits
Authenticated API responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`. Treat `rate_limited` as retry-after work, not a completed job.
Common errors
Expect `api_key_invalid` for revoked keys, `api_scope_forbidden` for missing scopes, source `blocked` or direct `export_blocked_by_qa` for hard source delivery blockers, and translation `export_blocked` when translated subtitle timing is invalid. Only hard timing or export blockers remove download URLs.