Thai AI voice for podcasts
Produce a Thai podcast from a script with one host or two, on a daily schedule, with chapter markers from timestamps, billed by the character.
Ideas to try
Every card plays a real clip generated from its exact script with Nom Yen. Open one in Speech to make it say anything.
How podcasters use the API
A Thai podcast is usually one or two people talking, and the person talking is the whole production. The show goes out when the host records, at the energy the host had that day. A daily schedule means recording every day, whether or not there is a voice left for it. A text-to-speech API makes the host a property of the script. One voice slug reads the show, a second slug is a second host, and both sound the same on episode 118 as on episode 1. A rewritten segment is billed by the characters it contains and the rest of the episode stays as it was. English names and product terms inside a Thai line read in the same voice. Sentence spans returned with the audio become chapter markers and a transcript for the show notes, and the file that comes back is the one the feed points at.
- Split the script by speakerEach host's run of lines is one part, and each part is one request carrying that host's voice slug. A part longer than the per-request ceiling splits again at a paragraph. A solo show is one slug all the way through.
- Render and stitchThe parts come back as MP3 files that concatenate in order, in ffmpeg or the editor the show already uses, with the music bed and stings placed as before. Ask for sentence spans on the same requests when the show wants chapter markers or a transcript.
- Publish as usualThe stitched file goes to the hosting service as the episode's enclosure. A daily show runs the same three steps from a script each morning. A correction regenerates one part and leaves the rest of the episode as it was.
Episode lengths, priced
| Show | Characters | Requests | Cost |
|---|---|---|---|
| One 20-minute episode | 12,120 | 3 | $0.18 |
| A weekly show, per month | 48,480 | 12 | $0.73 |
| A daily 20-minute show, per month | about $5.45 | ||
Computed from 606 characters per audio minute at $15 per million characters, 30 episodes to the month. Music beds, intros, and the edit are outside the figure. A rewritten segment is billed by its own characters alone.
Two hosts from one script
script.txt 12,120 chars (ราว 20 นาที) 01 host A oliang เปิดรายการ สรุปข่าว 02 host B sanaechan ช่วงคำถามจากผู้ฟัง 03 host A oliang ปิดรายการ → POST /v1/tts one request per part, the part's voice slug → part-01.mp3 part-02.mp3 part-03.mp3 → episode-118.mp3 (concatenated, music bed under)
A part is one speaker's run of the script. Each part is one request carrying that host's slug, and the parts join in order. Host A is the same slug in every episode, which is what keeps a show sounding like the same show.
What the feed points at
<item> <title>ตอนที่ 118</title> <enclosure url="https://cdn.example.com/ep118.mp3" type="audio/mpeg"/> <itunes:duration>20:00</itunes:duration> <podcast:chapters url="https://cdn.example.com/ep118.chapters.json"/> </item>
The enclosure is the stitched MP3. The chapters file is written from the sentence spans the API returned with each part, shifted by where that part starts in the episode. The hosting service you already use publishes the feed; nothing about it changes.
For developers
Send each part with its host's voice slug, and ask for sentence spans on the same call when the show wants chapter markers or a transcript.
curl -X POST https://api.paxalabs.com/v1/tts \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
"voice": "khanomkrok",
"model": "paxa-tts-flash-v1",
"timestamps": "word"
}' \
-o response.json
jq -r .audio response.json | base64 -d > speech.mp3
jq .timing.spans response.jsonimport { writeFile } from "node:fs/promises";
const response = await fetch("https://api.paxalabs.com/v1/tts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAXA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
text: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
voice: "khanomkrok",
model: "paxa-tts-flash-v1",
timestamps: "word",
}),
});
if (!response.ok) throw new Error(`TTS failed: ${response.status}`);
const { audio, timing } = await response.json();
await writeFile("speech.mp3", Buffer.from(audio, "base64"));
for (const span of timing.spans) {
console.log(`${span.start.toFixed(2)}s ${span.text}`);
}{
"audio": "SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYy...",
"timing": {
"granularity": "word",
"spans": [
{
"text": "สวัสดี",
"start": 0,
"end": 0.51
},
{
"text": "ครับ",
"start": 0.51,
"end": 0.85
},
{
"text": "ยินดี",
"start": 0.85,
"end": 1.27
},
{
"text": "ต้อนรับ",
"start": 1.27,
"end": 1.87
},
{
"text": "สู่",
"start": 1.87,
"end": 2.12
},
{
"text": "Paxa",
"start": 2.12,
"end": 2.46
},
{
"text": "Labs",
"start": 2.46,
"end": 2.8
}
]
}
}Hear the voices
OliangMale0:03
Relaxed low drawl for chill content and late-night radio.
ดึกแล้วนะครับ ชงกาแฟดำสักแก้ว แล้วฟังเพลงเบา ๆ ไปด้วยกันครับ
Khanom KrokMale0:03
Calm, unhurried elder voice and the roster's male lead: documentary, heritage storytelling, and narration.
สมัยผมยังหนุ่ม แถวนี้ยังเป็นทุ่งนา เดินไปตลาดทีหนึ่งก็ค่อนวันแล้วครับ
Nom YenFemale0:06
Bright, energetic voice and the roster's female lead: promos, social clips, and everyday product speech.
ยินดีต้อนรับสู่ Paxa Labs ค่ะ เสียงภาษาไทยที่สลับเป็น English ได้กลางประโยค อย่างเป็นธรรมชาติ
TakoFemale0:05
Friendly young narrator for audiobooks, recaps, and explainers.
ยามเช้าในกรุงเทพฯ แสงแดดอ่อน ๆ ทาบลงบนถนนที่เริ่มคึกคัก
A few of the 26 production voices, generated from the API with the published voice ids. The full roster plays on the Text-to-Speech page.
Two hosts, one request each
Paxa TTS Flash reads one voice per request. A two-host show is two slugs alternating through the script, and each host sounds the same on every episode. It reads long conversational passages as written, sentence-final particles included, and carries English names inside Thai lines without changing speaker.
Common questions
- Can two hosts talk in one episode?
- Yes. A request carries one voice. A two-host script alternates two slugs, one request per speaker turn or run of turns, and the parts join in order. Each host is the same slug in every episode.
- How long can an episode be?
- A request takes up to 5,000 characters. A 20-minute episode renders as 3 requests. The show itself has no length limit; per-plan rate limits are on the pricing page.
- Can we get chapter markers and a transcript?
- Yes. Ask for sentence or word spans on the same request and the response carries the audio with timing for every span, at a stated multiplier on the per-character rate. Shift each part's spans by where the part starts in the stitched file and they become chapters and a transcript.
- Does it read English names and terms inside Thai?
- Yes. Mixed Thai and English in one request reads in one voice. Product names, guest names, and English phrases inside a Thai line sound like the same host.
- Can we monetize a show made this way?
- On free credits alone, generated audio is for personal use and evaluation. Once you buy a plan or credits, the audio is yours for any use, including a sponsored or ad-supported show, and that covers episodes generated before the first purchase.
Start with free credits
Sign in with Google, GitHub, or Hugging Face and spend 100 one-time free credits on your own text.
More use cases