calesthio/amazon-polly
Use Amazon Polly for production text-to-speech work: selecting Standard, Neural, Long-form, or Generative engines and compatible voices; authoring SSML; creating speech marks for captions, word highlighting, or lip-sync; managing pronunciation lexicons; running synchronous, streaming, or asynchronous S3-backed synthesis; planning quotas, pricing, IAM, privacy, and QA for narration, audiobooks, accessibility audio, avatars, and multilingual media.
npx skills add https://github.com/calesthio/generative-media-skills --skill amazon-polly
Use Amazon Polly when the job needs AWS-hosted text-to-speech with predictable API operations, IAM governance, lexicons, SSML, and reusable generated audio. Treat Polly as a production TTS service, not a voice-cloning system: it provides AWS-managed voices and engines, not custom voice training or arbitrary speaker imitation.
Facts below were verified from official AWS documentation on 2026-07-10. Re-check AWS docs before relying on volatile facts such as prices, quotas, regions, voice lists, or engine feature compatibility.
Choose the operation first, because it defines latency, output custody, and timing options:
SynthesizeSpeech: real-time request/response. Use for short narration lines, UI prompts, voice preview, batch segment synthesis, and speech-mark JSON. Input limit is 6,000 total characters, with no more than 3,000 billed characters; output audio is cut off after 10 minutes. Documented fact: https://docs.aws.amazon.com/polly/latest/dg/limits.htmlStartSpeechSynthesisTask: asynchronous synthesis to Amazon S3, optionally with SNS notification. Use for long articles, audiobook chapters, training modules, and server workflows that should not hold an HTTP audio stream open. Input limit is 200,000 total characters, with no more than 100,000 billed characters. Documented fact: https://docs.aws.amazon.com/polly/latest/APIReference/API_StartSpeechSynthesisTask.htmlStartSpeechSynthesisStream: bidirectional streaming input/output over HTTP/2. Use only when the application needs streamed generative audio while text is still arriving. Documented fact: AWS currently documents this operation as supporting only the generative engine and not JSON speech marks. Source: https://docs.aws.amazon.com/polly/latest/APIReference/API_StartSpeechSynthesisStream.htmlThen select the engine:
standard: broadest compatibility, lowest listed commercial price, strong for utility prompts, IVR, high-volume notification audio, and workflows where cost/coverage matter more than expressiveness. AWS describes Standard voices as concatenative synthesis.neural: better naturalness than standard, with all speech marks, many SSML tags, asynchronous synthesis, and selected Newscaster speaking style support. Good default for produced narration when speech marks or SSML control matter.long-form: designed for longer narrative content such as articles, training, and marketing videos. AWS documents only six long-form voices as of 2026-07-10: en-US Danielle, Gregory, Ruth, Patrick and es-ES Alba, Raúl; region availability is US East (N. Virginia) only. Use when long-duration delivery quality matters and the voice/language constraints fit.generative: most conversational and adaptive Polly family, with streaming support and higher humanlike variability. Use for conversational assistant lines, virtual trainers, and ad-like reads when its feature constraints fit. Important documented boundaries: speech marks are not currently available; Newscaster style is not supported; AWS warns the model can vary after updates and includes a hallucination/emergency-stop safety mechanism that reduces but does not eliminate risk of inappropriate or truncated output. Source: https://docs.aws.amazon.com/polly/latest/dg/generative-voices.htmlDo not omit Engine in production. Some SDK/CLI paths default to standard; if the selected voice does not support Standard, the request can fail. Verify voice-engine-region compatibility with DescribeVoices or the current AWS voice pages before committing to a production voice.
Documented facts:
SynthesizeSpeech output formats include mp3, ogg_vorbis, ogg_opus, pcm, mulaw, alaw, and json; JSON is for speech marks. For SynthesizeSpeech, MP3/OGG Vorbis sample rates include 8000, 16000, 22050, 24000, 44100, and 48000 Hz; defaults are 22050 Hz for Standard and 24000 Hz for Neural, Long-form, and Generative. PCM is mono signed 16-bit little-endian with 8000 or 16000 Hz. Source: https://docs.aws.amazon.com/polly/latest/APIReference/API_SynthesizeSpeech.htmlStartSpeechSynthesisTask requires an output S3 bucket and can return task metadata including task ID, status, output URI, request characters, output format, sample rate, and SNS topic ARN. Source: https://docs.aws.amazon.com/polly/latest/APIReference/API_StartSpeechSynthesisTask.htmltime, type (sentence, word, viseme, or ssml), byte offsets for word/sentence marks, and a value. Source: https://docs.aws.amazon.com/polly/latest/dg/output.htmlSynthesizeSpeech 80 TPS for Standard, 8 TPS for Neural, 8 TPS for Long-form, 8 TPS for Generative; StartSpeechSynthesisTask 10 TPS for Standard/Neural, 1 TPS for Long-form/Generative; lexicon management 5 TPS combined; 100 lexicons per account per Region; single lexicon size 40,000 characters. Source: https://docs.aws.amazon.com/polly/latest/dg/limits.htmlProduction heuristics:
Use SSML when the script needs deterministic pauses, dates, acronyms, foreign-language spans, pronunciation fixes, or structural marks. Set TextType="ssml" and wrap the payload in <speak>...</speak>.
Documented facts:
<amazon:domain name="news">; Neural, Long-form, and Generative support many but not all tags. Unsupported tags can return errors. Source: https://docs.aws.amazon.com/polly/latest/dg/supportedtags.html<break> is fully available for Neural, Long-form, and Generative; individual <break> elements have a maximum duration of 10 seconds. Source: https://docs.aws.amazon.com/polly/latest/dg/limits.html<emphasis> is not available for Neural, Long-form, or Generative voices. Use wording, punctuation, <break>, or supported <prosody> controls instead. Source: https://docs.aws.amazon.com/polly/latest/dg/supportedtags.html<mark> is fully available for Neural and Long-form and partially available for Generative, but Generative speech-mark output is not currently available; do not design a Generative workflow that depends on mark JSON. Sources: https://docs.aws.amazon.com/polly/latest/dg/supportedtags.html, https://docs.aws.amazon.com/polly/latest/dg/generative-voices.html<say-as> is available for Long-form and Generative and partially available for Neural. For Neural, say-as interpret-as="characters" can cause the affected sentence to be synthesized with the corresponding Standard voice while still billed as Neural. Source: https://docs.aws.amazon.com/polly/latest/dg/say-as-tag.html&, <, and >. Source: https://docs.aws.amazon.com/polly/latest/dg/escapees.htmlProduction heuristics:
<prosody rate> or pitch controls.<break> tags.<phoneme> tags in every script.<lang> only for short phrases or names, and expect accented pronunciation if the voice is not native to the language.Example SSML for a product explainer:
<speak>
<p>
Meet <sub alias="Acme Cloud One">ACME C1</sub>.
<break time="250ms"/>
It turns noisy incident data into a ranked action plan.
</p>
<p>
In <say-as interpret-as="date" format="mdy">07-10-2026</say-as> tests,
teams cut triage time by <say-as interpret-as="cardinal">42</say-as> percent.
</p>
<p>
<mark name="cta_start"/>Start with one service, then expand.
</p>
</speak>
Why this structure works: <sub> avoids letter-by-letter acronym drift, <break> creates an edit point, <say-as> disambiguates data, and <mark> gives a deterministic cue for overlay timing when the chosen engine supports SSML speech marks.
Use speech marks when the downstream artifact needs timing:
word: karaoke captions, highlighted text, subtitle alignment, accessibility review.sentence: coarse captions or edit beats.ssml: cue points from <mark name="..."/> tags for visual overlays.viseme: mouth-shape timing for 2D/3D avatar rigs.Documented facts:
OutputFormat="json"; requesting marks with another output format is an error. Source: https://docs.aws.amazon.com/polly/latest/APIReference/API_SynthesizeSpeech.htmlProduction workflow:
OutputFormat="json" and the needed SpeechMarkTypes.For lip sync, map Polly viseme names to the rig’s mouth-shape set and smooth transitions. Visemes are not phoneme-level ground truth; add blend windows and test plosives/fricatives visually.
If the selected voice/engine cannot provide speech marks, use a different compatible voice/engine or run forced alignment/ASR after synthesis. This is a production workaround, not an AWS-documented equivalence.
Use lexicons for repeated pronunciation fixes: product names, acronyms, stylized spellings, names, jargon, and locale-specific terms.
Documented facts:
Production heuristics:
Example PLS lexicon:
<?xml version="1.0" encoding="UTF-8"?>
<lexicon version="1.0"
xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
alphabet="ipa"
xml:lang="en-US">
<lexeme>
<grapheme>ACME C1</grapheme>
<alias>Acme Cloud One</alias>
</lexeme>
<lexeme>
<grapheme>Qdrant</grapheme>
<phoneme>ˈkjuː drænt</phoneme>
</lexeme>
</lexicon>
For video editing, request a high enough sample rate to survive mixing and exports:
Production QA checklist:
Documented facts:
polly: prefix. Source: https://docs.aws.amazon.com/polly/latest/dg/security_iam_service-with-iam.htmlSynthesizeSpeech is stateless and its output cannot be retrieved from Polly later; store/encrypt generated files in your own system or S3 if you need retention. Source: https://docs.aws.amazon.com/polly/latest/dg/encryption-at-rest.htmlProduction security heuristics:
tts/dev/..., tts/prod/...) to simplify lifecycle policies and deletion.Documented boundary: Polly provides AWS voices; it is not a custom voice clone service. Production policy still matters:
Intent: produce a 30-second product narration with word captions and optional avatar mouth movement.
Decision:
neural, because speech marks are supported and naturalness is stronger than Standard.SynthesizeSpeech, because each paragraph is short.word, sentence, and viseme.Python sketch:
import boto3
polly = boto3.client("polly", region_name="us-east-1")
text = """<speak>
<p>Meet ACME Cloud One. <break time="200ms"/> It turns noisy incident data into a ranked action plan.</p>
<p><mark name="cta"/>Start with one service, then expand.</p>
</speak>"""
common = {
"Engine": "neural",
"VoiceId": "Joanna",
"Text": text,
"TextType": "ssml",
"SampleRate": "24000",
"LexiconNames": ["BrandLex"],
}
audio = polly.synthesize_speech(**common, OutputFormat="mp3")
with open("narration.mp3", "wb") as f:
f.write(audio["AudioStream"].read())
marks = polly.synthesize_speech(
**common,
OutputFormat="json",
SpeechMarkTypes=["word", "sentence", "viseme", "ssml"],
)
with open("narration.marks.jsonl", "wb") as f:
f.write(marks["AudioStream"].read())
QA: check that BrandLex exists in us-east-1, that the voice language matches the lexicon language, and that generated captions match the approved script.
Intent: convert a long training chapter into a single audio file without managing many short requests.
Decision:
StartSpeechSynthesisTask, because the chapter exceeds real-time SynthesizeSpeech limits.long-form only if the voice/language/region requirements fit; otherwise use neural and segment locally.Python sketch:
import boto3
polly = boto3.client("polly", region_name="us-east-1")
response = polly.start_speech_synthesis_task(
Engine="long-form",
VoiceId="Ruth",
TextType="ssml",
Text=open("chapter_01.ssml", encoding="utf-8").read(),
OutputFormat="mp3",
SampleRate="24000",
OutputS3BucketName="company-media-prod",
OutputS3KeyPrefix="tts/training-2026/chapter-01/",
SnsTopicArn="arn:aws:sns:us-east-1:123456789012:polly-render-events",
)
print(response["SynthesisTask"]["TaskId"])
print(response["SynthesisTask"].get("OutputUri"))
QA: poll GetSpeechSynthesisTask or subscribe to SNS, verify the output file exists under the expected S3 prefix, listen end-to-end, and compare duration against the chapter estimate.
User asks: “Use the most human Amazon Polly voice and give me exact word-level captions.”
Strong production response: explain that Generative may sound most conversational but currently does not support speech-mark generation, so exact word-level captions require either a Neural/Long-form voice with Polly marks or a post-synthesis alignment workflow. Recommend Neural if captions are mandatory and the voice is acceptable; choose Generative only if the user accepts ASR/forced-alignment timing as an additional QA step.
InvalidSsmlException: validate XML, escape reserved characters, remove unsupported tags for the chosen engine, and simplify nested markup.LexiconNotFoundException: check Region, lexicon name spelling, request order, and whether the synthesis client is using the same Region where the lexicon was uploaded.LanguageNotSupportedException or voice/engine error: call DescribeVoices with the intended engine and Region; do not assume a voice supports every engine.MarksNotSupportedForFormatException: request marks with OutputFormat="json" only, and generate audio in a separate request.SynthesizeSpeech, StartSpeechSynthesisTask, and StartSpeechSynthesisStream API references: https://docs.aws.amazon.com/polly/latest/APIReference/API_SynthesizeSpeech.html, https://docs.aws.amazon.com/polly/latest/APIReference/API_StartSpeechSynthesisTask.html, https://docs.aws.amazon.com/polly/latest/APIReference/API_StartSpeechSynthesisStream.htmlTake calesthio/amazon-polly from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.