---
title: "Dub an .srt subtitle file into a timecode-aligned WAV"
method: POST
path: "/api/v1/srt"
tags: ["Public API v1"]
---

# Dub an .srt subtitle file into a timecode-aligned WAV

`POST /api/v1/srt`

Synthesizes each subtitle cue and places it at its start timecode (silence-padded; overruns shift later cues). Billed by total submitted cue-text characters (~1 token/char, minimum 50, × the AI surcharge only when you opt in with `aiRefine=true`) — NOT output duration. With `aiRefine=true` the subtitles are moderated and each cue is pronunciation-normalized; by default cues are synthesized as provided, with no content check and no surcharge. Pass `engine` ("v3" default, "v4" premium — billed at its own multiplier) to choose the TTS engine. Tokens are deducted upfront and refunded if synthesis fails.

## Response `201`

The timecode-aligned track, plus per-cue placement showing where speed-fit or drift moved a cue.

- PublicSrtResponseDto
  - `success` boolean, required
  - `cuesCount` number, required — Number of subtitle cues synthesized.
  - `audioUrl` string, required — Presigned S3 download URL for the timecode-aligned WAV.
  - `audioUrlExpiresIn` number, required — Seconds until audioUrl expires.
  - `duration` number, required — Total duration of the dubbed track (seconds).
  - `sampleRate` number, required — Output sample rate.
  - `tokenCost` number, required — Tokens deducted (computed from total cue text).
  - `placements` PublicSrtPlacementDto[], required — Per-cue timeline placement (speed-fit + drift). May be empty — treat it as diagnostic detail, not as part of the result.
    - `index` number, required — 1-based cue index.
    - `scheduledStartMs` number, required — Start time from the subtitle (ms).
    - `actualStartMs` number, required — Start time after timeline assembly (ms).
    - `windowMs` number, required — Subtitle window length, end − start (ms).
    - `durationMs` number, required — Synthesized cue length after speed-fit (ms).
    - `speed` number, required — Tempo multiplier applied (1.0 = none).
    - `overflowed` boolean, required — True if the cue was pushed later than its start (drift).

## Other responses

- `400` — No file sent, an unparseable .srt, or an unknown voice.
- `401` — API key missing, malformed, or revoked.
- `403` — Your plan does not include SRT dubbing or this engine, or the grant is out of tokens / expired.
- `413` — Subtitle file larger than 10 MB.
- `422` — Content refused by moderation. Only reachable with `aiRefine=true`.
- `429` — Rate limit or token quota exceeded. When the limit came from the application the response carries `Retry-After` (seconds) and the `X-RateLimit-*` headers and is counted against your API key; a 429 with none of those headers came from the edge proxy and is counted against your source address, shared with every other key calling from it. Back off on either.

---

[API](https://skmtc.dev/vieneu/apis/vieneu-api.md) · [All operations](https://skmtc.dev/vieneu/apis/vieneu-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/vieneu/vieneu-api/revisions/11c8166a6c4d/schema)
