AI Text to Speech

How can we help?

Search the documentation or ask the AI agent anything about the plugin.

How audio generation works

Updated September 15, 2026

When you click Generate TTS, the browser sends one AJAX request (generate_tts) to admin-ajax.php. Inside that request the plugin prepares the text, splits it into chunks, calls OpenAI’s /v1/audio/speech endpoint once per chunk, joins the MP3 responses into a single file and saves it. This article describes that flow for developers and for diagnosing timeouts and cost.

1. Checks

The request needs a user who can edit posts (and the specific post), passes the Enabled for User Roles restriction, and a valid ai_tts_nonce. Generator page requests (no post) need manage_options.

2. Preparing the text

  1. Source: the saved post_content, or the Generator text passed through wp_kses_post().
  2. The ai_tts_post_content filter runs. PRO adds per-post customisation at priority 10 and global before/after text at 100.
  3. Twitter embeds are swapped for tweet text, then PRO custom pronunciations are applied.
  4. PRO splits the text on [voice="..."] and [/voice] into [ text, voice ] pairs.
  5. Each pair is cleaned: currency to words, images and URL prefixes removed, headings, lists and paragraph ends turned into spacing, [tts say] and [tts ignore] processed (PRO), all remaining shortcodes stripped, entities decoded, tags stripped and quotes removed.
  6. For posts (unless Replace Content is used), a title, author and date intro pair is added at the start with the main voice.
  7. The ai_tts_chunks filter runs on the pairs.

3. Splitting to API limits

Each pair is split so no chunk exceeds the limits from ai_tts_openai_tts_chunk_limits: 6,000 characters for gpt-4o-mini-tts or 2,000 for tts-1, and an estimated 1,800 tokens. Tokens are estimated as the larger of characters ÷ 3.5 and words × 1.35. The split point is the last paragraph break, sentence end, semicolon, colon, comma or space found in the second half of the allowed length.

For typical English text the character limit is reached before the token estimate, so chunks are up to about 6,000 characters on GPT-4o mini TTS. A 3,000-word article usually becomes three or four requests, plus one for the intro.

4. Calling OpenAI

Chunks are sent one after another, each with a 120-second timeout:

{
  "model": "gpt-4o-mini-tts",
  "input": "Chunk text...",
  "voice": "coral",
  "response_format": "mp3",
  "instructions": "Style preset + base guidance + custom instructions"
}

instructions is only sent for gpt-4o-mini-tts. Any error (HTTP 400 or above, a WordPress HTTP error, or an unexpected status) stops the whole generation immediately; nothing is saved, and completed chunks are still billed by OpenAI. A 429 with insufficient_quota gets its own message.

Because every chunk runs within one PHP request, total time is the sum of all chunks. Server or proxy timeouts shorter than that will cut the request off.

5. Saving the file

  1. One log entry per chunk is added to ai_tts_api_request_log.
  2. The MP3 responses are concatenated and written to /wp-content/uploads/ai-text-to-speech/ with WP_Filesystem.
  3. modify_ai_tts_file_url runs; with Dropbox selected, the file is uploaded, a public link is created and the local copy is deleted.
  4. For posts, ai_tts_file_url, ai_tts_generated_at and ai_tts_location meta are saved. Generator files are recorded in ai_tts_admin_generated_files.
  5. ai_tts_file_url filters the URL, which is returned as JSON to the browser.

Errors are passed through a sanitiser that redacts Bearer tokens and sk- keys, trimmed to 500 characters, written to the PHP error log with the prefix AI TTS: and added to the Error Log on the settings page.

Related

Was this article helpful?