---
title: "Create an async batch synthesis job"
method: POST
path: "/v1/audio/batches"
tags: ["audio"]
---

# Create an async batch synthesis job

`POST /v1/audio/batches`

## Request

- Base URL: `https://api.ditto.audio` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ditto.audio/v1/audio/batches`
- Auth: HTTP bearer

Accepts up to 1000 items (200000 characters total) and returns IMMEDIATELY with a batch id — no audio in this response. Poll `GET /v1/audio/batches/{id}` for progress, then read `GET /v1/audio/batches/{id}/items` (or stream `/output`) for per-item results and presigned MP3 URLs.

Only SHAPE is validated here — an item with no resolvable `model`/`voice`, or a batch over the character ceiling, is rejected with a 400 naming the offending index, because a job guaranteed to fail entirely is not worth creating. Synthesis failures are per-item results discovered while running and never fail the batch.

Output audio is deleted at `expiresAt` (24h by default). The batch and its per-item accounting survive that; the MP3s do not.

Pass `wait_ms` (up to 30000) to wait for the batch in the same request: a batch of at most 20 items that finishes in time is answered with its `items`, so a short synthesis is ONE request. Batches of at most 3 items run on an interactive queue and never wait behind bulk work.

## Request body

- CreateAudioBatchDto
  - `model` 'kokoro' | 'xtts' | 'pocket': Default model for every item that doesn’t set its own.
  - `voice` string: Default voice for every item that doesn’t set its own.
  - `language` string: Default language. Omit to infer per item from its own text.
  - `speed` number
  - `response_format` 'mp3' | 'mp3_64' | 'mp3_160' | 'wav' | 'wav24' | 'wav32f' | 'flac' | 'flac24' | 'pcm' | 'pcm_f32' | 'aiff' | 'caf' | 'au' | 'mulaw' | 'alaw': Default audio encoding for every item that doesn’t set its own. Every item is a COMPLETE file — real sizes in its header, not the streaming placeholder — so the result is directly playable and directly saveable. Hi-fidelity formats bill at 1.2× the normal rate, multiplied by the model’s own weighting — the audio is identical, but the bytes are several times larger. `mp3` (the default), `mp3_64`, `mulaw` and `alaw` are unchanged at 1×. Each format’s exact multiplier is in GET /v1/audio/format and in the `X-Ditto-Audio-Cost-Multiplier` response header.
  - `pauses` PausesDto
    - `sentence` number: Milliseconds after a sentence (. ! ? …). Setting it sends every sentence as its own engine call, so EVERY sentence boundary gets exactly this pause instead of the model’s own.
    - `clause` number: Milliseconds where a sentence too long for one engine call is cut at a comma, semicolon, colon or dash. Commas inside a call are paced by the model and are not affected.
    - `paragraph` number: Milliseconds at a blank line in `input`.
  - `items` SpeechBatchItemDto[], required: Up to 1000 items. They are accepted, persisted and worked through in the background — the response comes back immediately with a batch id, not with audio.
    - `id` string: Your own correlation id, echoed back on this item’s result. Never interpreted by us and never required to be unique — results also carry `index`, which always identifies the item unambiguously.
    - `model` 'kokoro' | 'xtts' | 'pocket': Overrides the batch default for this item.
    - `voice` string: Overrides the batch default for this item.
    - `input` string, required: The text to speak. The only required field.
    - `language` string: Overrides the batch default for this item. See GET /v1/audio/languages.
    - `speed` number: Overrides the batch default for this item.
    - `response_format` 'mp3' | 'mp3_64' | 'mp3_160' | 'wav' | 'wav24' | 'wav32f' | 'flac' | 'flac24' | 'pcm' | 'pcm_f32' | 'aiff' | 'caf' | 'au' | 'mulaw' | 'alaw': Overrides the batch default for this item — each result is its own complete file, so a batch may mix formats freely. Billed at this item’s own rate.
    - `pauses` PausesDto
      - `sentence` number: Milliseconds after a sentence (. ! ? …). Setting it sends every sentence as its own engine call, so EVERY sentence boundary gets exactly this pause instead of the model’s own.
      - `clause` number: Milliseconds where a sentence too long for one engine call is cut at a comma, semicolon, colon or dash. Commas inside a call are paced by the model and are not affected.
      - `paragraph` number: Milliseconds at a blank line in `input`.
  - `metadata` object: Opaque string map stored with the batch and returned on every read. We never interpret it — it exists so you can correlate a batch with whatever created it without keeping your own side table.
  - `wait_ms` number: Wait up to this many milliseconds (max 30000) for the batch to finish before answering. A batch that finishes in time, with at most 20 items, is answered WITH its `items` (presigned URLs included), so one short synthesis costs one request. Otherwise the batch is returned as it stands; poll with `GET /v1/audio/batches/{id}?wait_ms=`.

## Response `200`

The created batch, `status: "queued"`.

## Other responses

- `400`: MISSING_MODEL / UNKNOWN_MODEL / MISSING_VOICE / EMPTY_INPUT / BATCH_TOO_LARGE (message names the item index).
- `401`: Missing, malformed, revoked or expired credential.
- `402`: QUOTA_EXCEEDED — the batch would exceed the monthly allowance. See GET /v1/usage/quota.
- `403`: Key lacks the `audio:speech` scope.
- `429`: Rate limited (20/min per account), or TOO_MANY_ACTIVE_BATCHES — too much queued work already. Cancel or wait for one to finish.
- `503`: STORAGE_UNAVAILABLE — this deployment has nowhere to put batch output.

---

[API](https://skmtc.dev/ditto/apis/ditto-audio-api.md) · [All operations](https://skmtc.dev/ditto/apis/ditto-audio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ditto/apis/ditto-audio-api/revisions/30c8d7c218da?raw)
