Use Grok STT to transcribe an existing recording and Grok TTS to read a script in a preset voice. Try both in the SpicyAPI Playground first, then call them from code or an Agent. This is a saved-file workflow; for live back-and-forth voice you need a realtime transport. STT costs about $0.002 per minute of audio; TTS costs $0.018 per 1,000 characters. Based on our September 2026 tests, both routes handle adult recordings and scripts as written, with no refusals, masking or cut-off clips.
Prices, limits and what each route returns
| Grok STT | Grok TTS | |
|---|---|---|
| Model ID | xai/grok-stt/speech-to-text | xai/grok-tts/text-to-speech |
| Input | Audio URL, or an uploaded MP3/WAV up to 90 MB and 10 minutes; 1 second to 2 hours | Text, 1–15,000 characters |
| Languages | 25, or auto-detect | 20 plus auto-detect |
| Rate | $0.0000333334 per second | $0.018 per 1,000 characters |
| Result | Text plus word-timed transcript | One audio file |
| Kept for | Uploaded audio 1 day | Generated audio 14 days |
| Not available | Speaker labels | Voice cloning or reference-voice input |
A longer or larger recording needs a public URL. A URL can also point to M4A, FLAC, OGG, Opus, AAC, MP4 or MKV, but it must return the audio file itself, not a player or share page. Headerless PCM, μ-law and A-law are not accepted by STT.
An hour of audio costs about $0.12, and a 90-second recording about $0.003. The task first holds the two-hour maximum, about $0.24, and frees the difference once the real length is known. A 1,800-character script costs $0.0324; two full takes plus a 240-character pickup cost $0.06912. Spaces, punctuation and delivery tags all count as characters, and the quote shown before a TTS run is the final charge.
Try it in the Playground first
Transcribe. Open the Grok STT page and stay on the Playground tab.
- Under Input audio, upload your MP3 or WAV (up to 90 MB) or switch to Asset URL and press Add URL for a recording hosted elsewhere.
- Open Advanced parameters only if you need Language, Normalize text, Key terms, Keep filler words or Transcribe channels separately.
- Check the price estimate, then log in and run. The transcript appears in the Output panel, with a JSON view for the word timings.
Narrate. Open the Grok TTS page.
- Paste or edit the script in Text (the Sample script button fills an example), with any delivery tags.
- Choose a Voice; the menu labels each with name, gender and language. Open Advanced parameters for Speed, Output format, Bitrate and Language.
- Run it, play the take in the Output panel and use Download to keep it.
Try your hardest name or line here before you script a whole narration; what sounds wrong in the Playground will sound wrong from the API.
Transcription switches that change the result
Language (language) can be left out for detection. Normalize text (text_normalization) writes numbers, prices and dates as digits ("March 14", "$1,250") and requires language; leave it off when you need the exact spoken wording.
Keep filler words (filler_words: true) keeps "um" and "uh", which are dropped by default. Key terms (keyterms) bias spelling toward invented names and brands, up to 100 terms of 50 characters. Send them as objects, [{"term":"Mara"},{"term":"Jon"}], not as plain strings.
Transcribe channels separately (multichannel: true) transcribes each channel of a file separately, up to eight. With it off, a stereo recording with a different speaker on each side can come back with one speaker missing. Read output.transcript.channels for each side's words and timings; the combined text is one line per channel, not a finished dialogue. It does not separate two people mixed into one mono channel, and the route returns no speaker labels, so use separately recorded channels or a diarization tool for mono interviews.
Then call it from code
Install the Python SDK (pip install spicyapi, Python 3.11+), set SPICY_API_KEY, and run this transcription-to-narration flow. Replace the file path with a recording you are authorized to use.
import uuid
from spicyapi import SpicyClient, output_assets
client = SpicyClient() # reads SPICY_API_KEY from the environment
# 1. Transcribe a local recording (uploads are free and kept for one day)
source = client.upload_file("./interview.mp3")
stt = client.create_task(
model="xai/grok-stt/speech-to-text",
input_data={"audio_url": source["uri"], "multichannel": True},
idempotency_key=str(uuid.uuid4()), # new key per new request; reuse only to retry the same one
)
done = client.wait_for_terminal(stt["taskId"])
if done["state"] != "succeeded":
raise RuntimeError(f"STT task {stt['taskId']} ended as {done['state']}")
print(done["output"]["text"]) # edit this into your script
# 2. Narrate the edited script
script = "Chapter Three. The storm kept them in the cabin. [breath] <slow>Neither of them pretended to be reading.</slow>"
tts = client.create_task(
model="xai/grok-tts/text-to-speech",
input_data={"text": script, "voice_id": "rex", "language": "en", "speed": 0.9},
idempotency_key=str(uuid.uuid4()), # new key per new request; reuse only to retry the same one
)
final = client.wait_for_terminal(tts["taskId"])
if final["state"] != "succeeded":
raise RuntimeError(f"TTS task {tts['taskId']} ended as {final['state']}")
# 3. Copy the MP3 link to your own storage now; generated audio is kept for 14 days
print(output_assets(final)[0]["url"])Reuse an idempotency key only to retry the same request after a lost response. Uploads, waiting, errors, retries and renewing an expired download link are in the Python SDK reference and the task guide.
Or let an Agent run it
If an assistant should do the transcribing and narrating for you, give it the CLI or the MCP server. Both show the price and ask you to confirm before a paid call. The Skill adds instructions on top of either.
- CLI.
spicyapiruns one task from a terminal (npx @spicyapi/cliworks without installing):
``bash spicyapi files upload ./interview.mp3 # prints a spicy:// URI spicyapi tasks create --model xai/grok-stt/speech-to-text \ --input-json '{"audio_url": "spicy://YOUR-UPLOAD-URI", "multichannel": true}' --wait ``
- MCP server. Add the MCP server to your assistant, then ask: "Transcribe ./interview.mp3 with Grok STT, with each channel separate." It calls
spicyapi_upload_file, thenspicyapi_task_create(which stops for your price confirmation), thenspicyapi_task_wait.
``json { "mcpServers": { "spicyapi": { "command": "npx", "args": ["--yes", "--package=@spicyapi/mcp", "spicyapi-mcp"], "env": { "SPICY_API_KEY": "YOUR_SPICY_API_KEY" } } } } ``
- Skill.
npx skills add spicyapi-ai/spicy-skillinstalls the Agent Skill, which teaches a coding assistant to read the live schema and reuse idempotency keys. It cannot run tasks by itself, so use it with the CLI or MCP.
From transcript to script to voice
Edit the transcript before synthesis: fix names, punctuation and anything you want read differently. A short keyterm list is worth adding for invented names, but the transcript still reflects what the recording says, so check names against the audio.
TTS has 71 preset voices. eve is the default; eve, ara, leo, rex and sal speak every supported language, while the other 66 are tuned for one language and appear as opaque IDs in the API. Pick them from the voice menu on the Grok TTS model page, which labels each with name, gender and language. Try a short passage with your hardest name before synthesizing a whole narration.
Delivery is written into the script. Inline tags make a sound where you place them: [pause], [long-pause], [breath], [inhale], [exhale], [sigh], [laugh], [chuckle], [giggle], [cry]. Wrapping tags change how the enclosed words are read: <whisper>, <soft>, <slow>, <loud>, <build-intensity> and others. Tags are performed, not read aloud, but they count toward the price. For example:
{"text": "Chapter Three. The storm kept them in the cabin for two more days. [breath] By the second night, <slow>neither of them pretended to be reading anymore.</slow>", "voice_id": "rex", "speed": 0.9, "language": "en"}speed runs from 0.7 to 1.5. bitrate applies only to MP3. Use MP3 for playback and WAV for editing; raw pcm, mulaw and alaw at 8 kHz are for phone systems and will not play in a browser. SSML is not supported; use the tags above.
For a two-character scene, synthesize each speaker's lines separately with different voices and edit them together; one request reads one voice. Keep natural pauses at the joins instead of trimming them to zero.
To check a take, run the generated audio back through STT and compare it with the script: obvious missing or changed words, a cut-off ending or a tag spoken aloud usually show up, and a short clip costs a fraction of a cent. Then listen to the opening, the ending and any line it flags. If Grok's presets are not enough, see the other TTS options.
Common problems and fixes
| Symptom | Fix |
|---|---|
| Only one side of a stereo call appears | Enable multichannel and read output.transcript.channels |
| Two mono speakers cannot be separated | Record on separate channels or use a diarization tool |
keyterms rejected | Send objects with a term key, at most 100 |
| Normalization request rejected | Add a supported language code |
| MP3 bitrate rejected for WAV | Omit bitrate |
| Raw audio will not play | Use MP3 or WAV, or configure the raw format and sample rate in your player |
| STT rejects a URL | It must return the audio file itself, not an HTML page |
| Download link expired | Query the same task again for a fresh asset URL |
| Download returns 403 | Send a User-Agent header; the default Python one is blocked |
| Task still shows running | Keep waiting on the same task ID; do not create a replacement job |
STT word timestamps belong to the original recording. TTS returns audio without word timings, so changing the text or speed invalidates the old timestamps; align subtitles to the finished synthesized take.







