Polly and Transcribe: turn text into speech, then speech back into text
Amazon Polly reads text aloud and Amazon Transcribe writes speech down. Today: what each one is for, the October upgrade to Polly's Generative voices, and a lab that does a full round trip through one S3 bucket.
Amazon Polly with Amazon Transcribe, in plain words
Amazon Polly is text-to-speech: you send words, it returns audio. It has four engines with different quality and prices: standard, neural, long-form and generative, the most natural-sounding one. A quick request with SynthesizeSpeech returns audio right away but handles at most 3,000 characters. For longer text, StartSpeechSynthesisTask runs in the background and writes the audio file into an S3 bucket you choose (up to 100,000 billable characters per task).
Amazon Transcribe is the opposite: speech-to-text. A batch job reads an audio or video file from S3 (MP3, WAV, FLAC, MP4 and more), and writes a JSON transcript with the full text plus a timestamp and confidence score for each word. You can name the language, or let Transcribe detect it. If you don't give it an output bucket, the transcript goes to a bucket AWS manages, and you only get a download link that expires after 15 minutes.
Used together through Amazon S3, they make a handy round trip for testing voice features: generate a known sentence with Polly, transcribe it, and compare. In news: on October 20, 2026 AWS upgraded the model behind Polly's Generative engine for more natural speech, added 14 voices (including the US English voices Kenneth and Nate) and brought Hindi and Finnish to that engine for the first time. Existing SSML keeps working, but the docs warn that model updates can slightly change how a voice sounds.
Reach for it when
- You want to add spoken output to an app, such as reading articles aloud, phone-menu prompts or accessibility audio, and you'd rather pay per character than record a voice actor.
- You have recorded calls, meetings, podcasts or videos in S3 and need searchable text, captions or input for a summary.
- You are testing a voice workflow and want repeatable sample audio: Polly creates the same sentence on demand, and Transcribe checks what a listener (or a downstream model) would hear.
Skip it when
- You need a specific person's voice or your own brand voice. Polly gives you a fixed list of voices to choose from, so a custom voice is outside what this lab or the standard API covers.
- You need live, back-and-forth conversation with very low delay. Batch jobs take seconds to minutes; look at Polly's streaming APIs and Transcribe's streaming transcription instead.
- The audio is already human-transcribed or you only need a few minutes once. Typing it up or using a tool you already have may be simpler than setting up buckets and jobs.
The AI angle
Polly and Transcribe are the ears and mouth around a language model. A typical voice assistant on AWS transcribes what the user says, sends the text to a model in Amazon Bedrock, and has Polly speak the reply. Transcripts are also common inputs for Bedrock summaries or a Knowledge Base, so a meeting recording can become searchable notes. Treat transcripts as untrusted input when you pass them to a model, because the speaker controls what is said.
Hands-on lab: Round trip: Polly speaks a sentence into S3, Transcribe writes it back down
From AWS CloudShell, create a private S3 bucket, have Polly turn one sentence into an MP3 in that bucket with a Generative voice, have Transcribe turn the MP3 back into text in the same bucket, compare the two, and then delete everything.
Before you start
- A sandbox AWS account you own, signed in to the console with a user or role that may use Amazon S3, Amazon Polly and Amazon Transcribe.
- AWS CloudShell open in a Region where Polly's Generative engine is available, such as US East (N. Virginia) or US West (Oregon). In other Regions, change
--engine generativeto--engine neuralin step 2. - No access keys needed: CloudShell uses your console sign-in.
Set names and create a private bucket
Pick a Region and a unique bucket name. New S3 buckets block public access by default, and nothing in this lab changes that. The sentence is saved to a file so you can compare it later.
REGION=${AWS_REGION:-us-east-1} BUCKET=voice-lab-$(date +%s)-$RANDOM JOB=voice-lab-job-$(date +%s) echo "Region: $REGION Bucket: $BUCKET Job: $JOB" aws s3 mb s3://$BUCKET --region $REGION echo 'Welcome to the sandbox. Today we turn this sentence into speech, then back into text, and check that both versions match.' > original.txtAsk Polly to write the speech into S3
start-speech-synthesis-taskruns in the background and saves the MP3 in your bucket under a key that ends with the task ID. Ruth is one of the US English Generative voices.TASK_ID=$(aws polly start-speech-synthesis-task \ --region $REGION \ --engine generative \ --voice-id Ruth \ --output-format mp3 \ --output-s3-bucket-name $BUCKET \ --output-s3-key-prefix speech/ \ --text file://original.txt \ --query 'SynthesisTask.TaskId' --output text) echo "Polly task: $TASK_ID"Wait for Polly and find the audio file
Check the task until it says
completed(usually a few seconds; if it saysfailed, runaws polly get-speech-synthesis-task --region $REGION --task-id $TASK_IDto readTaskStatusReason). The task reports the file's address asOutputUri; the part after the bucket name is the S3 key.while true; do PSTATUS=$(aws polly get-speech-synthesis-task --region $REGION --task-id $TASK_ID \ --query 'SynthesisTask.TaskStatus' --output text) echo "Polly: $PSTATUS" [ "$PSTATUS" = "completed" ] || [ "$PSTATUS" = "failed" ] && break sleep 5 done URI=$(aws polly get-speech-synthesis-task --region $REGION --task-id $TASK_ID \ --query 'SynthesisTask.OutputUri' --output text) KEY=${URI#*/$BUCKET/} echo "Audio key: $KEY" aws s3 ls s3://$BUCKET --recursiveStart a Transcribe job on that MP3
Transcribe reads the file from the same bucket and writes its JSON result under
transcripts/. The bucket must be in the same Region as the job, which it is here.aws transcribe start-transcription-job \ --region $REGION \ --transcription-job-name $JOB \ --language-code en-US \ --media MediaFileUri=s3://$BUCKET/$KEY \ --media-format mp3 \ --output-bucket-name $BUCKET \ --output-key transcripts/ \ --query 'TranscriptionJob.TranscriptionJobStatus' --output textWait for Transcribe and read the transcript
Batch jobs usually finish within a minute for a clip this short. When the status is
COMPLETED, download the JSON and print the text next to the original.while true; do STATUS=$(aws transcribe get-transcription-job --region $REGION --transcription-job-name $JOB \ --query 'TranscriptionJob.TranscriptionJobStatus' --output text) echo "Transcribe: $STATUS" [ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ] && break sleep 10 done TKEY=$(aws s3api list-objects-v2 --bucket $BUCKET --prefix transcripts/ \ --query 'Contents[?ends_with(Key, `.json`)].Key | [0]' --output text) aws s3 cp s3://$BUCKET/$TKEY transcript.json echo 'Original:'; cat original.txt echo 'Transcript:' python3 -c "import json; print(json.load(open('transcript.json'))['results']['transcripts'][0]['transcript'])"Optional: listen to the voice
Copy the MP3 into CloudShell, then use Actions > Download file in CloudShell with the path
speech.mp3to play it on your computer.aws s3 cp s3://$BUCKET/$KEY speech.mp3 ls -l speech.mp3
Check that it worked
Step 3 shows completed and an OutputUri in your bucket, step 5 shows COMPLETED, and the transcript matches the original sentence word for word or with only small differences in punctuation.
Clean up
Delete the transcription job record, empty and remove the bucket (the MP3, the transcript and anything else Transcribe wrote there), and remove the local files. Polly tasks can't be deleted and need no cleanup: they are only a status record, and the CLI reference says a task stays available for 72 hours. The last commands should print Bucket gone and an empty list for the job.
aws transcribe delete-transcription-job --region $REGION --transcription-job-name $JOB
aws s3 rm s3://$BUCKET --recursive
aws s3 rb s3://$BUCKET
rm -f original.txt transcript.json speech.mp3
# Confirm nothing is left
aws s3 ls | grep "$BUCKET" || echo 'Bucket gone'
aws transcribe list-transcription-jobs --region $REGION --job-name-contains voice-lab \
--query 'TranscriptionJobSummaries[].TranscriptionJobName'Gotchas
- Not every voice works with every engine or Region. Asking for a voice the chosen engine doesn't support returns an error, and the Generative engine is offered only in a listed set of Regions. Check the voice list before you hard-code a voice ID.
- Without
--output-bucket-name, Transcribe stores the transcript in an AWS-managed bucket for 90 days and hands you a link that expires in 15 minutes. For anything you want to keep, write to your own bucket. - Transcription job names must be unique in your account and Region, and can't contain spaces. Reusing a name fails with a conflict error, so add a timestamp or delete the old job first.
- Polly's Generative voices don't produce speech marks (the word timing data used for lip-sync or highlighting) and don't have the newscaster style. Use a neural voice if you need those.
- Generative voices can change slightly when AWS updates the model, as it did in October 2026. If you produce audio in batches over months, such as a podcast season, listen for drift between batches.