---
title: Video captions
description: Burn captions onto an existing public video URL with style, language, and optional script alignment.
---

Standalone video captions take a public HTTPS video URL and return a job-backed
captioned video. Prefer this when you already have a finished clip. For Avatar
Video, you can also enable inline `captions` on
[talking-video](/models/avatar-videos) or store caption intent on
[previews](/models/avatar-video-previews).

```text
POST /v1/video-captions
GET  /v1/video-captions/:id
```

## Create a caption job

Required: `video_url`. Optional: `style`, `font`, `language`, `script_text`,
`words`, `cues` / `segments` (authored overlay; skips speech-to-text),
plus the usual `mode` / `webhook_url` / `wait_timeout_seconds` communication
fields.

Speech-to-captions (`script_text`, or STT when omitted) needs audible speech. A
silent clip fails as `caption_no_speech` (`next_action: use_overlay_captions`)
— not a generic policy reject. Pass `cues` (or `segments`) with `text` +
`start` + `end` to burn authored overlay copy without ASR.

<!-- api-call-example:video-caption-create -->

### Style, font and language

| Field | Values / default |
|---|---|
| `style` | Omit it and the wording decides: `slam` for Latin, `black-outline` for Korean. Or name `slam`, `punch`, `tiktok-green`, `korean-ad` (CapCut-style Hangul karaoke for Korean speech: one short phrase at a time, lower-third, the spoken word shifts to a heavy weight — pair with `language: "ko"`; Korean `script_text` is supported), plus the Hangul identities below |
| `font` | Optional Hangul face for the chosen style. Omit to keep that style's own face. Hangul styles only — see [Fonts](#fonts). |
| `design` | Optional per-request overrides of the style's colours, typography, placement, phrasing and motion — see [Design overrides](#design-overrides). |
| `language` | Speech-to-text hint (`ko`, `en`, …). Omit for automatic detection. |

`style` picks the look and the motion, `design` edits it, `font` picks the face
it is drawn in, and `language` only tells speech-to-text what to expect.
**`language` never selects the style or the font.** An omitted `style` does
follow the wording — Korean copy resolves to `black-outline` rather than burning
tofu through the Latin default — but a style you name is rendered as named.

### Design overrides

A style is a set of design tokens, and `design` overrides them for one request.
Every field is optional and merges over the style's own value, so one key changes
one thing:

```json
{
  "video_url": "https://example.com/clean.mp4",
  "style": "black-outline",
  "design": { "colors": { "active": "#22D3EE" } }
}
```

That burns `black-outline` with a cyan emphasis and everything else untouched.

| Group | Fields |
|---|---|
| `colors` | `base`, `active`, `stroke`, `accent`, `accent_deep`, `card` (`null` draws no card) |
| `typography` | `base_weight`, `active_weight`, `active_scale`, `font_size_ratio`, `safe_width_ratio`, `stroke_width_px` |
| `placement` | `anchor_ratio`, `landscape_anchor_ratio` — line centre as a fraction of frame height |
| `phrasing` | `max_words`, `max_chars`, `pause_seconds` |
| `motion` | `enter_seconds`, `exit_seconds`, `emphasis_in_seconds`, `emphasis_out_seconds` |

Colours are hex, `rgb()`/`rgba()`, or `transparent`; other CSS syntax is
rejected rather than drawn into the render document. Numbers outside their
documented range are a `400`, so a look fails at request time instead of
rendering wrong and billing. `design` is not supported on `punch` or
`tiktok-green`, which still render on a path that reads none of these tokens.

### Korean text on a Latin style is rejected

`slam`, `punch` and `tiktok-green` draw in Latin display faces with no Hangul
glyphs. Sending Korean copy to one of them returns `400`
(`caption_hangul_text_latin_style`) rather than re-styling the job: a caption you
did not choose is worse than an error, and the alternative — a video full of
tofu boxes — costs the same as a good one. Latin copy on `slam` is unchanged.

The same applies to `font`: the faces below are Hangul, so naming one alongside
a Latin style returns `400` (`caption_font_requires_hangul_style`).

### Hangul caption identities

For Korean **speech** — talking head, UGC, creator voiceover — use one of these
rather than `slam`, whose Latin display face has no Hangul glyphs and renders
Korean as tofu. They group words into phrase cards instead of one word at a
time, and burn the transcript exactly as written with no case folding.

| Style | Look |
|---|---|
| `black-outline` | CapCut white fill on a thick black outline, mid-frame. The safe default. |
| `weight-shift` | Phrase cards where the spoken word takes the weight and the rest drops back. |
| `highlight` | An accent block sweeps in behind the word being spoken. |
| `pill-karaoke` | The card sits in a dark pill; colour tracks the voice. |
| `clip-wipe` | Each word wiped in left-to-right. Clearest at small phone sizes. |
| `editorial-emphasis` | Left-aligned two-line card: the lead words stay small and the phrase-final word drops to a second line at roughly twice the size, in a display face, sliding in from the margin. |

`korean-ad` is the ad karaoke look (weight-shift, with an accent colour on the
spoken word). It is **not** what an omitted `style` resolves to — that is
`black-outline` — so ask for `korean-ad` when you want the ad treatment.

An omitted `style` also drops the accent colour: both default styles were
authored with a gold spoken-word tint, and a look nobody asked for should not
introduce one, so a defaulted render keeps the fill colour on the spoken word.
Naming `black-outline` (or `slam`) keeps that style's own gold tint, and
`design.colors.active` sets it either way.

### Fonts

Optional. Omitting `font` keeps the style's own face — Pretendard for
`korean-ad`, `weight-shift`, `highlight`, `pill-karaoke` and
`editorial-emphasis`, Do Hyeon for `black-outline` and `clip-wipe`. Setting it
keeps the style and swaps the face.

`editorial-emphasis` draws its emphasis line in Black Han Sans whatever `font`
says, because that identity's look is the contrast between its two faces rather
than either one of them. `font` moves its lead line, like it moves the single
line everywhere else.

| `font` | Family | Feel |
|---|---|---|
| `pretendard` | Pretendard Variable | Clean baseline |
| `do-hyeon` | Do Hyeon | Thick rounded, the CapCut classic |
| `black-han-sans` | Black Han Sans | Impact |
| `jua` | Jua | Soft cute rounded |
| `dunggeunmo` | DungGeunMo | Pixel / retro |
| `bagel-fat-one` | Bagel Fat One | Fat rounded |
| `dongle` | Dongle Bold | Playful rounded display |
| `gasoek-one` | Gasoek One | Ultra-thick impact |
| `yeon-sung` | Yeon Sung | Brushy |
| `single-day` | Single Day | Soft cute handwritten |
| `hi-melody` | Hi Melody | Soft rounded cute |
| `nanum-pen` | Nanum Pen Script | Handwritten |
| `gowun-dodum` | Gowun Dodum | Soft editorial |

All of them are SIL Open Font License 1.1 and ship with the renderer. Any other
name is rejected rather than substituted, so a caption never falls back to a
face you did not ask for.

`weight-shift` and `korean-ad` animate the `wght` axis, which only Pretendard
carries; on a static face those styles keep their colour and scale emphasis and
lose the weight travel.

### Restyling without transcribing twice

To re-burn the same video under a different style, pass `source_caption_id`
instead of `video_url`:

```json
{ "source_caption_id": "…", "style": "black-outline" }
```

Sume reuses that caption's source video and the word timings it already has, so
no second speech-to-text runs. Pass `words` alongside it only to correct the
wording. Billing is unchanged — a restyle is still a render.

You can also supply `words` (word-level) or `cues` / `segments` (phrase-level
overlay cards) with `text`, `start`, `end` in seconds. Either form skips
speech-to-text and burns exactly that copy at those times — this is the path
for silent clips. `script_text`, `words`, `cues`, and `segments` are mutually
exclusive.

### Optional `script_text`

When provided, Sume keeps speech-to-text word timings as the timing source of
truth and aligns burned-in wording to your script. Alignment can fail with
typed public job errors:

- `script_alignment_mismatch`
- `script_alignment_failed`

Suggested next action: `simplify_script_text_or_omit`. Omit `script_text` to
burn STT wording instead.

```bash
curl -X POST https://api.sume.com/v1/video-captions \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-caption-script-001" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/example/clean.mp4",
    "style": "punch",
    "script_text": "Say hello to the Sume developer platform."
  }'
```

## Pricing note

Each accepted standalone caption job reserves and captures **$0.20 USD** of
Sume usage for videos up to 60 seconds under the current fixed estimate.
Confirm live pricing in `GET /v1/catalog` and OpenAPI.

Inline Avatar Video captions are a separate add-on on the avatar-video estimate
and do **not** create a `video_caption` resource.

## Poll and read

```bash
curl https://api.sume.com/v1/jobs/job_123/status \
  -H "Authorization: Bearer $SUME_API_KEY"

curl https://api.sume.com/v1/jobs/job_123/result \
  -H "Authorization: Bearer $SUME_API_KEY"

curl https://api.sume.com/v1/video-captions/vc_123 \
  -H "Authorization: Bearer $SUME_API_KEY"
```

The resource returns public-safe status, style, and captioned `video_url` /
artifacts when ready. Raw transcripts, renderer internals, and signed source
URLs are not part of the public contract.

## Constraints

- `video_url` must be a fetchable public HTTPS video URL.
- Localhost, private-network, non-HTTPS, signed/private, and provider task URLs
  are rejected.
- Raw cue lists, SRT uploads, and provider task ids are unsupported.

## Related

- [Generate avatar video](/models/avatar-videos) (inline captions)
- [Avatar video previews](/models/avatar-video-previews)
- [Media inputs](/workflows/asset-library)
- [Jobs and results](/workflows/jobs-and-results)
