Voice samples
Generate a spoken sample of any voice from your own text.
Voice samples turn a short piece of your own text into speech in any ThunderPhone voice — any catalog voice or any of your organization's ready custom voices. Use them to audition voices, preview a greeting before an agent goes live, or let your users hear a voice say their own words.
Samples are meant for exactly that — sampling. Text is capped at 500 characters and generation is rate limited per organization. If you need longer samples or a higher limit, contact support@thunderphone.com.
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /v1/voices/preview | Generate a sample and get a signed audio URL |
GET | /v1/voices/previews/{id} | Stream a generated sample's MP3 |
Generate a sample
curl -X POST https://api.thunderphone.com/v1/voices/preview \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"voice": "sarah",
"language": "en",
"text": "Thanks for calling Northwind Dental. How can I help you today?"
}'| Field | Type | Required | Description |
|---|---|---|---|
voice | string | yes | A voice name from GET /v1/voices — a catalog voice like sarah, or one of your ready custom voices like custom:cv_2f6f90b0e9a34ee8b39be7d1 |
language | string | yes | Language code for the sample. For a custom voice, this must be the clone's language |
text | string | yes | The text to speak, 1–500 characters |
{
"id": "vp_9c2f90b0e9a34ee8b39be7d1",
"url": "https://api.thunderphone.com/v1/voices/previews/vp_9c2f90b0e9a34ee8b39be7d1?sig=...",
"cached": false,
"charged_cents": 3,
"expires_at": "2026-08-07T14:12:08.317Z"
}| Field | Type | Description |
|---|---|---|
id | string | Identifier of the generated sample |
url | string | Signed URL that streams the sample's MP3 without authentication |
cached | boolean | true when the sample was served from cache instead of freshly generated |
charged_cents | integer | What this request cost: 3 for a fresh generation, 0 for a cache hit |
expires_at | timestamp | ISO 8601 time the signed URL stops working |
Pricing and caching
A fresh generation costs 3¢ from your prepaid balance. Repeating an
identical request — same voice, language, and text — is served from cache,
costs nothing, and returns "cached": true with a fresh signed URL. That
also means expired URLs are cheap to replace: re-send the same request and
you get a new one free.
Generation is limited to 30 fresh samples per organization per hour; cache hits don't count against the limit.
Fetch the audio
The url in the response is a signed link to:
GET /v1/voices/previews/{id}?sig={signature}
It streams audio/mpeg without an Authorization header, so you can hand it
straight to an <audio> element or a media player. Signed URLs are valid
for 7 days. You can also call the endpoint without sig while
authenticated:
curl \
https://api.thunderphone.com/v1/voices/previews/vp_9c2f90b0e9a34ee8b39be7d1 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
--output sample.mp3An unsigned, unauthenticated request — or one with an invalid or expired
signature — returns 401 Unauthorized. A sample generated for another
organization's custom voice returns 404 Not Found.
Errors
| Status | Code or response | When |
|---|---|---|
400 | {"voice":"Voice is required."} | Voice is missing |
400 | {"language":"Language is required."} | Language is missing |
400 | {"text":"Text must be between 1 and 500 characters."} | Text is empty or too long |
400 | {"voice":"Voice is not available."} | Custom voice doesn't exist, isn't ready, or belongs to another organization |
400 | {"language":"Language is not available for this voice."} | The voice doesn't speak that language |
402 | insufficient_balance | Prepaid balance is below the sample price |
422 | preview_unavailable_language | Sample generation isn't available for this voice in that language yet |
429 | preview_rate_limited | The organization used its 30 fresh generations this hour |
The 402, 422, and 429 responses share one shape:
{
"code": "preview_rate_limited",
"detail": "Sample generation limit reached. Try again later."
}A 422 means the voice exists but ThunderPhone can't generate ad-hoc
samples for it in that language yet — the voice still works normally on
calls. Pick another voice or language for the sample.