> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inworld.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Synthesize speech (batch)

> Submit many independent synthesis requests as one background job and immediately receive a [long-running operation](https://google.aip.dev/151). Poll the operation via the Get operation endpoint until `done` is `true`, then download the results file named by `resultsUri` — it lists every item's outcome, keyed by the `customId` you assigned. Each item's `request` is an ordinary synthesis request, validated exactly as the async endpoint would validate it; one invalid item rejects the whole batch at submit. That includes `timestampType` in a language without alignment support — like async jobs, a batch item never returns audio with requested timestamps silently missing.



## OpenAPI

````yaml post /tts/v1/voice:synthesizeBatch
openapi: 3.0.0
info:
  title: Inworld Text-to-Speech API
  version: v1
  contact:
    name: Inworld AI
    url: https://inworld.ai
    email: support@inworld.ai
servers:
  - url: https://api.inworld.ai
security:
  - inworld_basic: []
tags:
  - name: TextToSpeech
  - name: AudioPromptPreparationService
  - name: SpeechToPhonemesService
paths:
  /tts/v1/voice:synthesizeBatch:
    post:
      tags:
        - TextToSpeech
      summary: Synthesize speech (batch)
      description: >-
        Submit many independent synthesis requests as one background job and
        immediately receive a [long-running
        operation](https://google.aip.dev/151). Poll the operation via the Get
        operation endpoint until `done` is `true`, then download the results
        file named by `resultsUri` — it lists every item's outcome, keyed by the
        `customId` you assigned. Each item's `request` is an ordinary synthesis
        request, validated exactly as the async endpoint would validate it; one
        invalid item rejects the whole batch at submit. That includes
        `timestampType` in a language without alignment support — like async
        jobs, a batch item never returns audio with requested timestamps
        silently missing.
      operationId: TextToSpeech_SynthesizeSpeechBatch
      requestBody:
        $ref: '#/components/requestBodies/ttsv1SynthesizeSpeechBatchRequest'
      responses:
        '200':
          description: >-
            The batch was accepted. The returned operation is not yet done; poll
            it to track progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/longrunningOperation'
              examples:
                submitted:
                  summary: Batch accepted
                  value:
                    name: >-
                      workspaces/{workspace}/ttsBatchJobs/9e03dcb0-2ad5-483b-9e61-962f3a9bb430/operations/1786040024834-Wpj18s
                    metadata: null
                    done: false
        4XX:
          description: >-
            An unexpected error response. A rejected item is named by its
            position and its `customId`, for example `items[1] (custom_id
            "chapter-02"): Unknown voice: Nope not found!`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/rpcStatus'
      x-codeSamples:
        - lang: bash
          label: cURL
          source: >-
            curl --location
            'https://api.inworld.ai/tts/v1/voice:synthesizeBatch' \

            --header "Authorization: Basic $INWORLD_API_KEY" \

            --header 'Content-Type: application/json' \

            --data '{
              "items": [
                {
                  "customId": "chapter-01",
                  "request": {
                    "text": "The first chapter begins on a quiet morning.",
                    "voiceId": "Ashley",
                    "modelId": "inworld-tts-2",
                    "audioConfig": { "audioEncoding": "MP3" },
                    "timestampType": "WORD"
                  }
                },
                {
                  "customId": "chapter-02",
                  "request": {
                    "text": "The second chapter opens with a storm at sea.",
                    "voiceId": "Dennis",
                    "modelId": "inworld-tts-2",
                    "audioConfig": { "audioEncoding": "MP3" }
                  }
                }
              ]
            }'
        - lang: python
          label: Python
          source: >-
            import requests


            url = "https://api.inworld.ai/tts/v1/voice:synthesizeBatch"

            headers = {"Authorization": "Basic <api-key>", "Content-Type":
            "application/json"}



            def item(custom_id, text, voice_id):
                return {
                    "customId": custom_id,
                    "request": {
                        "text": text,
                        "voiceId": voice_id,
                        "modelId": "inworld-tts-2",
                        "audioConfig": {"audioEncoding": "MP3"},
                    },
                }


            payload = {"items": [
                item("chapter-01", "The first chapter begins on a quiet morning.", "Ashley"),
                item("chapter-02", "The second chapter opens with a storm at sea.", "Dennis"),
            ]}


            operation = requests.post(url, json=payload, headers=headers).json()

            print(operation["name"])  # poll this via GET /lro/v1alpha/{name}
        - lang: javascript
          label: JavaScript
          source: >-
            const url = 'https://api.inworld.ai/tts/v1/voice:synthesizeBatch';


            const item = (customId, text, voiceId) => ({
              customId,
              request: {
                text,
                voiceId,
                modelId: 'inworld-tts-2',
                audioConfig: { audioEncoding: 'MP3' },
              },
            });


            const response = await fetch(url, {
              method: 'POST',
              headers: {
                'Authorization': 'Basic <api-key>',
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                items: [
                  item('chapter-01', 'The first chapter begins on a quiet morning.', 'Ashley'),
                  item('chapter-02', 'The second chapter opens with a storm at sea.', 'Dennis'),
                ],
              }),
            });


            const operation = await response.json();

            console.log(operation.name); // poll this via GET
            /lro/v1alpha/{name}
components:
  requestBodies:
    ttsv1SynthesizeSpeechBatchRequest:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ttsv1SynthesizeSpeechBatchRequest'
          example:
            items:
              - customId: chapter-01
                request:
                  text: The first chapter begins on a quiet morning.
                  voiceId: Ashley
                  modelId: inworld-tts-2
                  audioConfig:
                    audioEncoding: MP3
                  timestampType: WORD
              - customId: chapter-02
                request:
                  text: The second chapter opens with a storm at sea.
                  voiceId: Dennis
                  modelId: inworld-tts-2
                  audioConfig:
                    audioEncoding: MP3
      required: true
  schemas:
    longrunningOperation:
      type: object
      description: >-
        A [google.longrunning.Operation](https://google.aip.dev/151) tracking an
        asynchronous or batch synthesis job.
      properties:
        name:
          type: string
          description: >-
            Server-assigned operation resource name, in the format
            `workspaces/{workspace}/ttsAsyncJobs/{job}/operations/{operation}`
            for async jobs or
            `workspaces/{workspace}/ttsBatchJobs/{batch}/operations/{operation}`
            for batch jobs. Pass it verbatim as the path of the Get operation
            endpoint to poll for completion.
          example: >-
            workspaces/{workspace}/ttsAsyncJobs/8f14e45f-ceea-4673-93d8-04f724c8a1b2/operations/1784837936461-p0sEhU
        metadata:
          description: >-
            Service-specific metadata associated with the operation. Not
            populated for TTS jobs yet; job metadata is planned for a later
            release, so do not write code that depends on this field staying
            absent.
          nullable: true
          allOf:
            - $ref: '#/components/schemas/protobufAny'
        done:
          type: boolean
          description: >-
            If `false`, the job is still running. If `true`, the job has
            finished and exactly one of `error` or `response` is set.
        error:
          $ref: '#/components/schemas/rpcStatus'
        response:
          description: >-
            Set when the job succeeded. Its `@type` identifies which kind: an
            async job carries SynthesizeSpeechAsyncResponse, a batch job
            SynthesizeSpeechBatchResponse.
          oneOf:
            - $ref: '#/components/schemas/ttsv1SynthesizeSpeechAsyncResponse'
            - $ref: '#/components/schemas/ttsv1SynthesizeSpeechBatchResponse'
    rpcStatus:
      type: object
      properties:
        code:
          type: integer
          format: int32
          description: >-
            The error code, as specified by [gRPC status
            codes](https://grpc.io/docs/guides/status-codes/).
          example: 5
        message:
          type: string
          description: A short description of the error.
          example: 'Unknown voice: John not found!'
        details:
          type: array
          items:
            $ref: '#/components/schemas/protobufAny'
          example: []
    ttsv1SynthesizeSpeechBatchRequest:
      type: object
      description: >-
        A set of independent synthesis requests submitted as one job. Items may
        each use a different voice, model, language, and audio format.
      required:
        - items
      properties:
        items:
          type: array
          description: >-
            The requests to synthesize, between 1 and 10000. The whole request
            must also fit the 16 MiB message limit, which caps the aggregate
            around 4M characters — the item ceiling is only reachable when items
            are short.
          minItems: 1
          maxItems: 10000
          items:
            $ref: '#/components/schemas/ttsv1SynthesizeSpeechItem'
    protobufAny:
      type: object
      properties:
        '@type':
          type: string
      additionalProperties: {}
    ttsv1SynthesizeSpeechAsyncResponse:
      type: object
      description: >-
        Result of a completed asynchronous synthesis job, packed into the
        operation's `response` field. The download URLs are pre-signed and
        time-limited: fetch them without an `Authorization` header, and before
        `expireTime`.
      properties:
        '@type':
          type: string
          description: >-
            Type of the serialized message, always
            `type.googleapis.com/ai.inworld.tts.v1.SynthesizeSpeechAsyncResponse`.
          example: type.googleapis.com/ai.inworld.tts.v1.SynthesizeSpeechAsyncResponse
        audioUri:
          type: string
          description: >-
            Pre-signed download URL for the synthesized audio, in the format
            requested via `audioConfig.audioEncoding`.
          example: https://storage.googleapis.com/…/audio.mp3?X-Goog-Signature=…
        timestampsUri:
          type: string
          description: >-
            Pre-signed download URL for the timestamp alignment JSON. Only
            present when `timestampType` was set in the request.
          example: https://storage.googleapis.com/…/timestamps.json?X-Goog-Signature=…
        expireTime:
          type: string
          format: date-time
          description: >-
            Time at which the download URLs expire, approximately 7 days after
            job completion.
          example: '2026-07-30T12:00:00Z'
    ttsv1SynthesizeSpeechBatchResponse:
      type: object
      description: >-
        Result of a completed batch, packed into the operation's `response`
        field. A batch's per-item URLs do not fit in one response, so this names
        a results *file* instead. The URL is pre-signed and time-limited: fetch
        it without an `Authorization` header, and before `expireTime`.
      properties:
        '@type':
          type: string
          description: >-
            Type of the serialized message, always
            `type.googleapis.com/ai.inworld.tts.v1.SynthesizeSpeechBatchResponse`.
          example: type.googleapis.com/ai.inworld.tts.v1.SynthesizeSpeechBatchResponse
        resultsUri:
          type: string
          description: >-
            Pre-signed download URL for the results file, whose contents are
            described by SynthesizeSpeechBatchResults.
          example: https://storage.googleapis.com/…/results.json?X-Goog-Signature=…
        expireTime:
          type: string
          format: date-time
          description: >-
            Time at which the results file and every audio URL inside it expire,
            approximately 7 days after the batch completes.
          example: '2026-08-13T18:13:47Z'
    ttsv1SynthesizeSpeechItem:
      type: object
      description: >-
        One synthesis request within a batch, paired with the key you use to
        find its result.
      required:
        - customId
        - request
      properties:
        customId:
          type: string
          description: >-
            Your key for this item, echoed into the results file. Required, and
            unique within the batch — it is the only thing correlating a result
            back to what you submitted. Treat it as opaque; the service never
            interprets it.
          example: chapter-01
        request:
          description: >-
            The synthesis request for this item, identical in shape to a
            synchronous request and subject to the same validation, including
            the 100000-character text ceiling. On-Demand accounts are
            additionally capped at 10000 characters across the whole batch.
          allOf:
            - $ref: '#/components/schemas/ttsv1SynthesizeSpeechRequest'
    ttsv1SynthesizeSpeechRequest:
      type: object
      properties:
        text:
          type: string
          description: >-
            The text to be synthesized into speech. Maximum input of 2,000
            characters.
        voiceId:
          type: string
          description: The ID of the voice to use for synthesizing speech.
        audioConfig:
          $ref: '#/components/schemas/ttsv1AudioConfig'
        modelId:
          type: string
          description: >-
            The ID of the model to use for synthesizing speech. See
            [Models](../../../tts/tts-models) for available models.
        language:
          type: string
          description: >-
            BCP-47 language tag (e.g., `en-US`, `fr-FR`, `ja-JP`) specifying the
            language that the given voice should speak the text in. Matching is
            case- and separator-insensitive for standard two-part tags (`en-gb`,
            `EN_GB`, and `en-GB` are equivalent); longer tags with extension
            subtags must match a catalog entry exactly. If a localized voice
            prompt exists for the language, it will be used. When omitted, the
            original voice prompt will be used and the language will be
            auto-detected from the input text. If an invalid language code is
            provided, an error will be returned.


            See [Languages](../../../tts/capabilities/multilingual) for more
            details.
        deliveryMode:
          type: string
          enum:
            - DELIVERY_MODE_UNSPECIFIED
            - STABLE
            - BALANCED
            - CREATIVE
          default: DELIVERY_MODE_UNSPECIFIED
          description: >-
            *Only supported by `inworld-tts-2`. The field is ignored on other
            models.*


            Controls how varied the output is. 


            - `DELIVERY_MODE_UNSPECIFIED`: Defaults to `BALANCED` behavior.

            - `STABLE`: Optimizes for more consistent, predictable output.

            - `BALANCED`: Balanced between stability and diversity.

            - `CREATIVE`: Optimizes for increased emotional range and variation.
        instruction:
          type: string
          description: >-
            *Only supported by `inworld-tts-2`. The field is ignored on other
            models.*


            Speaking-style instruction for this request — for example `speak
            loudly and urgently` or `sound out of breath`. Applies to the whole
            request. Write it in English, even when `text` is in another
            language. An empty string means unset.


            You can also change the instruction mid-text with inline `[bracket]`
            tags. A tag applies from where it appears until you change it, so it
            overrides this field from that point on; `[reset]` removes the
            instruction for the rest of the text. Prefer one approach or the
            other rather than combining them.


            See [Steering](../../../tts/capabilities/steering) for the full
            guide.
        temperature:
          type: number
          format: float
          default: 1
          description: >-
            *Ignored on `inworld-tts-2`. Use
            [`deliveryMode`](#body-delivery-mode) instead.*


            Determines the degree of randomness when sampling audio tokens to
            generate the response.


            Defaults to 1.0. Accepts values between 0 (exclusive) and 2
            (inclusive). Higher values will make the output more random and can
            lead to more expressive results. Lower values will make it more
            deterministic. If 0 is provided, the default value will be used.


            For the most stable results, we recommend using the default value.
        timestampType:
          type: string
          enum:
            - TIMESTAMP_TYPE_UNSPECIFIED
            - WORD
            - CHARACTER
          default: TIMESTAMP_TYPE_UNSPECIFIED
          description: >-
            Controls timestamp metadata returned with the audio. When enabled,
            the response includes timing arrays, which can be useful for
            word-highlighting, karaoke-style captions, and lipsync.


            - WORD: Output arrays under `timestampInfo.wordAlignment` (words,
            wordStartTimeSeconds, wordEndTimeSeconds).

            - CHARACTER: Output arrays under `timestampInfo.characterAlignment`
            (characters, characterStartTimeSeconds, characterEndTimeSeconds).

            - TIMESTAMP_TYPE_UNSPECIFIED: Do not compute alignment; timestamp
            arrays will be empty or omitted.


            **Phonetic details:** `phoneticDetails` is currently only returned
            for **WORD** alignment (not CHARACTER).


            **Latency note:** Alignment adds additional computation. Enabling
            alignment can increase latency.
        applyTextNormalization:
          type: string
          enum:
            - APPLY_TEXT_NORMALIZATION_UNSPECIFIED
            - 'ON'
            - 'OFF'
          default: APPLY_TEXT_NORMALIZATION_UNSPECIFIED
          description: >-
            When enabled, text normalization automatically expands and
            standardizes things like numbers, dates, times, and abbreviations
            before converting them to speech. For example, Dr. Smith becomes
            Doctor Smith, and 3/10/25 is spoken as March tenth, twenty
            twenty-five. Turning this off may reduce latency, but the speech
            output will read the text exactly as written. Defaults to
            automatically deciding whether to apply text normalization.
        enhanceGeneration:
          type: boolean
          default: false
          description: >-
            When `true`, applies denoising to the synthesized audio to reduce
            background noise and artifacts, improving the overall audio quality
            of the generation. Defaults to `false` (no denoising).
        synthesisContext:
          $ref: '#/components/schemas/ttsv1SynthesisContext'
      required:
        - text
        - voiceId
        - modelId
    ttsv1AudioConfig:
      type: object
      properties:
        audioEncoding:
          $ref: '#/components/schemas/v1AudioConfigAudioEncoding'
        bitRate:
          type: integer
          format: int32
          description: >-
            Bits per second of the audio. Only for compressed audio formats
            (`MP3`, `OGG_OPUS`). The default is 128,000.
        sampleRateHertz:
          type: integer
          format: int32
          description: >-
            The synthesis sample rate (in hertz) for this audio. Accepts values
            within the range [8000, 48000]. Supported sample rates are: 8000,
            16000, 22050, 24000, 32000, 44100, 48000.

             When this is specified, if this is different from the voice's natural sample rate, then the audio will be converted to the desired sample rate (which might result in worse audio quality), unless the specified sample rate is not supported for the encoding chosen, in which case it will fail the request and return an error. The default is 48,000.
        speakingRate:
          type: number
          format: double
          description: >-
            Speaking rate/speed, in the range [0.5, 1.5]. The default is 1.0,
            which is the normal native speed supported by the specific voice. We
            recommend using values above 0.8 to ensure high quality.
      description: Configurations to use when synthesizing speech.
    ttsv1SynthesisContext:
      type: object
      description: >-
        Context for the current synthesis request. Supplying the text of earlier
        requests from the same session or conversation gives the model
        additional context and can improve the quality of the generation,
        especially for short or ambiguous input text.
      properties:
        previousRequests:
          type: array
          description: >-
            Previous requests from the same session or conversation, in the
            order they were synthesized.
          items:
            type: object
            properties:
              text:
                type: string
                description: The text that was synthesized in the previous request.
    v1AudioConfigAudioEncoding:
      type: string
      enum:
        - LINEAR16
        - MP3
        - OGG_OPUS
        - ALAW
        - MULAW
        - FLAC
        - PCM
        - WAV
      default: MP3
      description: |-
        The desired output format of the synthesized audio. Defaults to `MP3`.
         - `LINEAR16`: Uncompressed 16-bit signed little-endian samples (Linear PCM). For non-streaming, the WAV header is included in the response. For streaming, the WAV header is included in every audio chunk.
         - `MP3`: MP3 audio.
         - `OGG_OPUS`: Opus encoded audio wrapped in an ogg container. The result will be a file which can be played natively on Android, and in browsers (at least Chrome and Firefox). The quality of the encoding is considerably higher than MP3 while using approximately the same bitrate.
         - `ALAW`: ALAW encoded audio. 8-bit companded PCM.
         - `MULAW`: MULAW encoded audio. 8-bit companded PCM.
         - `FLAC`: FLAC encoded audio. Lossless audio format.
         - `PCM`: PCM audio. Uncompressed 16-bit signed little-endian samples with no WAV header.
         - `WAV`: WAV audio. Uncompressed 16-bit signed little-endian samples. For non-streaming, the WAV header is included in the response. For streaming, the WAV header is included in the first audio chunk only.
  securitySchemes:
    inworld_basic:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Your [authentication](../../../api-reference/introduction) credentials.
        For Basic authentication, please populate `Basic $INWORLD_API_KEY`. You
        can create a key in one command with the [Inworld
        CLI](../../../tts/resources/inworld-cli): `inworld workspace add-key`.

````