{"openapi":"3.1.0","info":{"title":"ResponsiveVoice TTS API","version":"2.1.0","description":"Text-to-speech API with a multilingual built-in voice catalog, streaming support, and bring-your-own-key access to premium provider voices.\n\n## Quick Start (Browser)\n\n```html\n<script src=\"https://cdn.responsivevoice.org/sdk/latest/responsivevoice.js\"></script>\n<script>\n  responsiveVoice.init({ apiKey: 'YOUR_API_KEY' });\n  responsiveVoice.speak('Hello world', 'UK English Female');\n</script>\n```\n","license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://texttospeech.responsivevoice.org","description":"Current"}],"components":{"securitySchemes":{"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Public website identifier. Send together with X-API-Secret."},"apiSecretHeader":{"type":"apiKey","in":"header","name":"X-API-Secret","description":"Non-public server credential. Send together with X-API-Key."}},"schemas":{"HealthStatus":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]},"WebsiteConfig":{"type":"object","properties":{"features":{"$ref":"#/components/schemas/WebsiteFeatures"},"voice":{"$ref":"#/components/schemas/WebsiteVoice"},"analytics":{"$ref":"#/components/schemas/WebsiteAnalytics"},"sdkVersion":{"type":"string","enum":["v1","v2"],"default":"v1"}}},"WebsiteFeatures":{"type":"object","properties":{"welcomeMessage":{"$ref":"#/components/schemas/WelcomeMessageFeature"},"speakSelectedText":{"$ref":"#/components/schemas/ToggleFeature"},"speakLinks":{"$ref":"#/components/schemas/ToggleFeature"},"speakInactivity":{"$ref":"#/components/schemas/TextFeature"},"speakEndPage":{"$ref":"#/components/schemas/TextFeature"},"exitIntent":{"$ref":"#/components/schemas/TextFeature"},"accessibilityNavigation":{"$ref":"#/components/schemas/ToggleFeature"},"paragraphNavigation":{"$ref":"#/components/schemas/ToggleFeature"},"webPlayer":{"$ref":"#/components/schemas/WebPlayerFeature"},"welcomeMessageOnce":{"type":"boolean","default":false}},"default":{"welcomeMessage":{"enabled":false,"text":null},"speakSelectedText":{"enabled":false},"speakLinks":{"enabled":false},"speakInactivity":{"enabled":false,"text":null},"speakEndPage":{"enabled":false,"text":null},"exitIntent":{"enabled":false,"text":null},"accessibilityNavigation":{"enabled":false},"paragraphNavigation":{"enabled":false},"webPlayer":{"enabled":false,"selector":"article","paragraphSelector":"p, h2, h3, li","position":"before","theme":"neutral","controls":{"progress":true,"time":true,"skip":true,"speed":true,"brand":{"icon":true,"poweredBy":true}},"navigation":{"paragraphHighlight":true,"paragraphClick":true},"layout":{"mode":"shrink","display":"block"},"miniPlayer":{"enabled":true,"position":"bottom-left","animation":"slide"},"sanitize":{"enabled":true,"exclude":[]}},"welcomeMessageOnce":false}},"WelcomeMessageFeature":{"type":"object","properties":{"enabled":{"type":"boolean","default":false},"text":{"type":["string","null"],"default":null}},"default":{"enabled":false,"text":null}},"ToggleFeature":{"type":"object","properties":{"enabled":{"type":"boolean","default":false}},"default":{"enabled":false}},"TextFeature":{"type":"object","properties":{"enabled":{"type":"boolean","default":false},"text":{"type":["string","null"],"default":null}},"default":{"enabled":false,"text":null}},"WebPlayerFeature":{"type":"object","properties":{"enabled":{"type":"boolean","default":false},"selector":{"type":"string","default":"article"},"paragraphSelector":{"type":"string","default":"p, h2, h3, li"},"position":{"$ref":"#/components/schemas/WebPlayerPosition"},"theme":{"$ref":"#/components/schemas/WebPlayerTheme"},"controls":{"$ref":"#/components/schemas/WebPlayerControls"},"navigation":{"$ref":"#/components/schemas/WebPlayerNavigation"},"layout":{"$ref":"#/components/schemas/WebPlayerLayout"},"miniPlayer":{"$ref":"#/components/schemas/MiniPlayer"},"voice":{"$ref":"#/components/schemas/VoiceSelector"},"sanitize":{"$ref":"#/components/schemas/WebPlayerSanitize"},"pitch":{"type":"number","minimum":0,"maximum":2},"rate":{"type":"number","minimum":0,"maximum":2},"volume":{"type":"number","minimum":0,"maximum":1}},"default":{"enabled":false,"selector":"article","paragraphSelector":"p, h2, h3, li","position":"before","theme":"neutral","controls":{"progress":true,"time":true,"skip":true,"speed":true,"brand":{"icon":true,"poweredBy":true}},"navigation":{"paragraphHighlight":true,"paragraphClick":true},"layout":{"mode":"shrink","display":"block"},"miniPlayer":{"enabled":true,"position":"bottom-left","animation":"slide"},"sanitize":{"enabled":true,"exclude":[]}}},"WebPlayerPosition":{"anyOf":[{"type":"string","enum":["inline","before","after"]},{"$ref":"#/components/schemas/WebPlayerMountTarget"}],"default":"before"},"WebPlayerMountTarget":{"type":"object","properties":{"target":{"type":"string"},"at":{"type":"string","enum":["inside","before","after"],"default":"inside"}},"required":["target"]},"WebPlayerTheme":{"anyOf":[{"type":"string","enum":["neutral","responsivevoice"]},{"$ref":"#/components/schemas/WebPlayerThemeTokens"}],"default":"neutral"},"WebPlayerThemeTokens":{"type":"object","properties":{"bg":{"type":"string"},"fg":{"type":"string"},"muted":{"type":"string"},"accent":{"type":"string"},"accentSoft":{"type":"string"},"hover":{"type":"string"},"border":{"type":"string"},"track":{"type":"string"},"fill":{"type":"string"}}},"WebPlayerControls":{"type":"object","properties":{"progress":{"type":"boolean","default":true},"time":{"type":"boolean","default":true},"skip":{"type":"boolean","default":true},"speed":{"type":"boolean","default":true},"brand":{"$ref":"#/components/schemas/WebPlayerBrand"}},"default":{"progress":true,"time":true,"skip":true,"speed":true,"brand":{"icon":true,"poweredBy":true}}},"WebPlayerBrand":{"type":"object","properties":{"icon":{"type":"boolean","default":true},"poweredBy":{"type":"boolean","default":true}},"default":{"icon":true,"poweredBy":true}},"WebPlayerNavigation":{"type":"object","properties":{"paragraphHighlight":{"type":"boolean","default":true},"paragraphClick":{"type":"boolean","default":true}},"default":{"paragraphHighlight":true,"paragraphClick":true}},"WebPlayerLayout":{"type":"object","properties":{"mode":{"type":"string","enum":["shrink","fill"],"default":"shrink"},"display":{"type":"string","enum":["inline","block"],"default":"block"}},"default":{"mode":"shrink","display":"block"}},"MiniPlayer":{"type":"object","properties":{"enabled":{"type":"boolean","default":true},"position":{"anyOf":[{"$ref":"#/components/schemas/MiniPlayerCorner"},{"$ref":"#/components/schemas/MiniPlayerOffset"}],"default":"bottom-left"},"animation":{"type":"string","enum":["none","fade","slide","pop"],"default":"slide"}},"default":{"enabled":true,"position":"bottom-left","animation":"slide"}},"MiniPlayerCorner":{"type":"string","enum":["top-left","top-right","bottom-left","bottom-right"]},"MiniPlayerOffset":{"type":"object","properties":{"top":{"type":"string"},"right":{"type":"string"},"bottom":{"type":"string"},"left":{"type":"string"}}},"VoiceSelector":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/RegexSelector"},{"$ref":"#/components/schemas/VoiceQuery"}]},"RegexSelector":{"type":"object","properties":{"regex":{"type":"string"},"flags":{"type":"string"}},"required":["regex"]},"VoiceQuery":{"type":"object","properties":{"name":{"type":"string"},"lang":{"type":"string"},"gender":{"type":"string","enum":["f","m","male","female"]},"isByok":{"type":"boolean"},"provider":{"type":"string"}}},"WebPlayerSanitize":{"type":"object","properties":{"enabled":{"type":"boolean","default":true},"exclude":{"type":"array","items":{"type":"string"},"default":[]}},"default":{"enabled":true,"exclude":[]}},"WebsiteVoice":{"type":"object","properties":{"name":{"type":"string","default":"UK English Female"},"pitch":{"type":"number","minimum":0,"maximum":2,"default":1},"rate":{"type":"number","minimum":0,"maximum":2,"default":1},"volume":{"type":"number","minimum":0,"maximum":1,"default":1}},"default":{"name":"UK English Female","pitch":1,"rate":1,"volume":1}},"WebsiteAnalytics":{"type":"object","properties":{"enabled":{"type":"boolean","default":false}},"default":{"enabled":false}},"ErrorResponse":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ApiError"}},"required":["error"],"description":"API error response"},"ApiError":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"statusCode":{"type":"number"},"errors":{}},"required":["message","statusCode"],"description":"API error detail"},"VoicesListResponse":{"type":"object","properties":{"voices":{"type":"array","items":{"$ref":"#/components/schemas/VoiceWithLinks"}},"systemVoices":{"type":"array","items":{"$ref":"#/components/schemas/SystemVoice"}},"count":{"type":"number"},"_links":{"$ref":"#/components/schemas/CollectionLinks"}},"required":["voices","systemVoices","count","_links"],"description":"Voice collection with HATEOAS navigation"},"VoiceWithLinks":{"type":"object","properties":{"name":{"type":"string"},"flag":{"type":"string"},"gender":{"type":"string","enum":["f","m"]},"lang":{"type":"string"},"voiceIDs":{"type":"array","items":{"type":"number"}},"deprecated":{"type":"boolean"},"isByok":{"type":"boolean"},"provider":{"type":"string"},"_links":{"$ref":"#/components/schemas/VoiceLinks"}},"required":["name","flag","gender","lang","voiceIDs","_links"],"description":"User-facing voice with HATEOAS links"},"VoiceLinks":{"type":"object","properties":{"self":{"$ref":"#/components/schemas/HrefLink"},"synthesize":{"$ref":"#/components/schemas/SynthesizeLink"},"synthesize:stream":{"$ref":"#/components/schemas/SynthesizeStreamLink"},"stream:websocket":{"$ref":"#/components/schemas/StreamWebsocketLink"}},"required":["self","synthesize","synthesize:stream","stream:websocket"],"description":"HATEOAS links on a voice resource"},"HrefLink":{"type":"object","properties":{"href":{"type":"string"}},"required":["href"],"description":"HATEOAS link with absolute URL"},"SynthesizeLink":{"type":"object","properties":{"href":{"type":"string"},"method":{"type":"string","enum":["POST"]},"body":{"type":"object","properties":{"voice":{"type":"string"},"lang":{"type":"string"}},"required":["voice"]}},"required":["href","method","body"],"description":"HATEOAS synthesize action link"},"SynthesizeStreamLink":{"type":"object","properties":{"href":{"type":"string"},"method":{"type":"string","enum":["POST"]},"body":{"type":"object","properties":{"voice":{"type":"string"},"lang":{"type":"string"},"stream":{"type":"boolean"}},"required":["voice","stream"]},"accept":{"type":"string","enum":["text/event-stream"]},"premium":{"type":"boolean"}},"required":["href","method","body","accept","premium"],"description":"HATEOAS streaming synthesize link (HTTP audio streaming)"},"StreamWebsocketLink":{"type":"object","properties":{"href":{"type":"string"},"protocol":{"type":"string","enum":["websocket"]},"message":{"type":"object","properties":{"type":{"type":"string"},"voice":{"type":"string"},"lang":{"type":"string"}},"required":["type","voice"]},"premium":{"type":"boolean"}},"required":["href","protocol","message","premium"],"description":"HATEOAS WebSocket stream link"},"SystemVoice":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"},"lang":{"type":"string"},"rate":{"type":"number"},"pitch":{"type":"number"},"timerSpeed":{"type":"number"},"fallbackVoice":{"type":"boolean"},"service":{"$ref":"#/components/schemas/TTSService"},"voiceName":{"type":"string"},"gender":{"type":"string"},"volume":{"type":"number"},"deprecated":{"type":"boolean"}},"required":["id","name"],"description":"Low-level system voice mapping"},"TTSService":{"type":"string","enum":["g1","g2","g3","g5","gwn","msv","oai"],"description":"Available TTS service backends"},"CollectionLinks":{"type":"object","properties":{"self":{"$ref":"#/components/schemas/HrefLink"}},"required":["self"],"description":"HATEOAS links on the voice collection"},"VoicesByLanguageResponse":{"type":"object","properties":{"language":{"type":"string"},"voices":{"type":"array","items":{"$ref":"#/components/schemas/VoiceWithLinks"}},"count":{"type":"number"},"_links":{"$ref":"#/components/schemas/ByLanguageLinks"}},"required":["language","voices","count","_links"],"description":"Voices for a specific language with HATEOAS navigation"},"ByLanguageLinks":{"type":"object","properties":{"self":{"$ref":"#/components/schemas/HrefLink"},"allVoices":{"$ref":"#/components/schemas/HrefLink"}},"required":["self","allVoices"],"description":"HATEOAS links on a by-language voice collection"},"VoiceDetailResponse":{"type":"object","properties":{"voice":{"$ref":"#/components/schemas/Voice"},"_links":{"$ref":"#/components/schemas/VoiceDetailLinks"}},"required":["voice","_links"],"description":"Single voice detail with HATEOAS navigation"},"Voice":{"type":"object","properties":{"name":{"type":"string"},"flag":{"type":"string"},"gender":{"type":"string","enum":["f","m"]},"lang":{"type":"string"},"voiceIDs":{"type":"array","items":{"type":"number"}},"deprecated":{"type":"boolean"},"isByok":{"type":"boolean"},"provider":{"type":"string"}},"required":["name","flag","gender","lang","voiceIDs"],"description":"User-facing voice definition"},"VoiceDetailLinks":{"type":"object","properties":{"self":{"$ref":"#/components/schemas/HrefLink"},"synthesize":{"$ref":"#/components/schemas/SynthesizeLink"},"synthesize:stream":{"$ref":"#/components/schemas/SynthesizeStreamLink"},"stream:websocket":{"$ref":"#/components/schemas/StreamWebsocketLink"},"collection":{"$ref":"#/components/schemas/HrefLink"},"byLanguage":{"$ref":"#/components/schemas/HrefLink"}},"required":["self","synthesize","synthesize:stream","stream:websocket","collection","byLanguage"],"description":"HATEOAS links on a voice detail response"},"SynthesizeRequest":{"type":"object","properties":{"text":{"type":"string"},"voice":{"type":"string"},"lang":{"type":"string"},"engine":{"$ref":"#/components/schemas/TTSService"},"name":{"type":"string"},"gender":{"$ref":"#/components/schemas/VoiceGender"},"pitch":{"type":"number","minimum":0,"maximum":2},"rate":{"type":"number","minimum":0,"maximum":2},"volume":{"type":"number","minimum":0,"maximum":1},"format":{"$ref":"#/components/schemas/AudioFormat"}},"required":["text"],"description":"Text-to-speech synthesis request"},"VoiceGender":{"type":"string","enum":["m","male","f","female"],"description":"Voice gender classification"},"AudioFormat":{"type":"string","enum":["mp3","ogg","wav"],"description":"Supported audio output formats"}},"parameters":{}},"paths":{"/health":{"get":{"tags":["System"],"summary":"Health check","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl https://texttospeech.responsivevoice.org/health"}],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthStatus"}}}}}}},"/v2/config":{"get":{"tags":["Configuration"],"summary":"Get website configuration","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Returns the website configuration including feature flags and voice settings for the authenticated user.","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl https://texttospeech.responsivevoice.org/v2/config -H 'X-API-Key: YOUR_API_KEY' -H 'X-API-Secret: YOUR_API_SECRET'"},{"lang":"javascript","label":"Node.js","source":"import { ResponsiveVoiceAPIClient } from '@responsivevoice/api-client';\n\nconst client = new ResponsiveVoiceAPIClient({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' });\nconst config = await client.getConfig();"}],"responses":{"200":{"description":"Website configuration with feature flags, voice settings (name, pitch, rate, volume), and sdkVersion.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebsiteConfig"}}}},"401":{"description":"API key required — all config requests must be authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/voices":{"get":{"tags":["Voices"],"summary":"List all voices","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Returns the full voice collection: user-facing responsive voices and internal system voices used for voice resolution. When platform parameters are provided, platform-specific availability rules are applied — voices unavailable on the given browser/OS combination are excluded. Also includes premium BYOK (Bring Your Own Key) provider voices when the user has configured API credentials for their website. Each voice in the response includes HATEOAS _links for self-discovery, synthesis, and streaming navigation.","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl https://texttospeech.responsivevoice.org/v2/voices -H 'X-API-Key: YOUR_API_KEY' -H 'X-API-Secret: YOUR_API_SECRET'"},{"lang":"javascript","label":"Node.js","source":"import { ResponsiveVoiceAPIClient } from '@responsivevoice/api-client';\n\nconst client = new ResponsiveVoiceAPIClient({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' });\nconst { voices } = await client.getVoices();"}],"parameters":[{"schema":{"type":"string"},"required":false,"description":"Browser name (e.g. \"Chrome\", \"Firefox\", \"Safari\"). Used to filter voices by platform-specific availability rules.","name":"browser","in":"query"},{"schema":{"type":"string"},"required":false,"description":"Browser version string (e.g. \"120.0\"). Refines platform filtering for version-specific voice support.","name":"browserVersion","in":"query"},{"schema":{"type":"string"},"required":false,"description":"Operating system name (e.g. \"Windows\", \"macOS\", \"Android\", \"iOS\"). Used to filter voices by OS-specific availability.","name":"os","in":"query"},{"schema":{"type":"string"},"required":false,"description":"OS version string (e.g. \"10\", \"14.2\"). Refines platform filtering for version-specific voice support.","name":"osVersion","in":"query"}],"responses":{"200":{"description":"Voice collection containing user-facing voices with HATEOAS navigation links, system voices for internal resolution, and a total count. Supports ETag-based conditional requests.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoicesListResponse"}}}},"304":{"description":"Not modified (ETag match)"},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/voices/by-language/{lang}":{"get":{"tags":["Voices"],"summary":"Get voices by language","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Returns all voices available for a specific BCP-47 language code (e.g. \"en-US\", \"pt-BR\", \"ja-JP\"). Includes both free and premium voices. Each voice includes HATEOAS _links for navigation.","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl https://texttospeech.responsivevoice.org/v2/voices/by-language/en-US -H 'X-API-Key: YOUR_API_KEY' -H 'X-API-Secret: YOUR_API_SECRET'"},{"lang":"javascript","label":"Node.js","source":"import { ResponsiveVoiceAPIClient } from '@responsivevoice/api-client';\n\nconst client = new ResponsiveVoiceAPIClient({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' });\nconst { voices } = await client.getVoicesByLanguage('en-US');"}],"parameters":[{"schema":{"type":"string"},"required":true,"description":"BCP-47 language code (e.g. \"en-US\", \"fr-FR\", \"zh-CN\"). Case-sensitive.","name":"lang","in":"path"}],"responses":{"200":{"description":"Voices matching the requested language, with count and HATEOAS navigation links. Supports ETag-based conditional requests.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoicesByLanguageResponse"}}}},"304":{"description":"Not modified (ETag match)"},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/voices/{name}":{"get":{"tags":["Voices"],"summary":"Get voice by name","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Returns a specific voice by its exact name (e.g. \"UK English Female\"). The response includes HATEOAS _links with a self link, a synthesize link for generating audio with this voice, and a stream link for real-time streaming synthesis.","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl https://texttospeech.responsivevoice.org/v2/voices/UK%20English%20Female -H 'X-API-Key: YOUR_API_KEY' -H 'X-API-Secret: YOUR_API_SECRET'"},{"lang":"javascript","label":"Node.js","source":"import { ResponsiveVoiceAPIClient } from '@responsivevoice/api-client';\n\nconst client = new ResponsiveVoiceAPIClient({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' });\nconst voice = await client.getVoice('UK English Female');"}],"parameters":[{"schema":{"type":"string"},"required":true,"description":"Exact voice name (e.g. \"UK English Female\", \"US English Male\"). URL-encode spaces as %20.","name":"name","in":"path"}],"responses":{"200":{"description":"Voice details with HATEOAS navigation links (self, synthesize, stream). Supports ETag-based conditional requests.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceDetailResponse"}}}},"304":{"description":"Not modified — the resource has not changed since the last request (ETag match)"},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No voice found with the given name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/text/synthesize":{"get":{"tags":["Synthesis"],"summary":"Synthesize text to speech (GET)","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Converts text to speech audio via query parameters. This endpoint is idempotent and CDN-cacheable — responses include Cache-Control headers with long TTLs (1 day client, 7 days CDN). Prefer this over POST for deterministic requests where caching is beneficial. The same parameters always produce the same audio, making it safe for CDN edge caching.","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl \"https://texttospeech.responsivevoice.org/v2/text/synthesize?text=Hello+world&voice=US+English+Female\" -H 'X-API-Key: YOUR_API_KEY' -H 'X-API-Secret: YOUR_API_SECRET' --output speech.mp3"},{"lang":"javascript","label":"Node.js","source":"import { ResponsiveVoiceAPIClient } from '@responsivevoice/api-client';\n\nconst client = new ResponsiveVoiceAPIClient({ apiKey: 'YOUR_API_KEY', apiSecret: 'YOUR_API_SECRET' });\nconst audio = await client.synthesize({ text: 'Hello world', voice: 'US English Female' });"}],"parameters":[{"schema":{"type":"string"},"required":true,"description":"The text to convert to speech. Supports plain text and SSML for compatible engines.","name":"text","in":"query"},{"schema":{"type":"string"},"required":false,"description":"ResponsiveVoice name (e.g. \"UK English Male\"). Resolved server-side to engine + lang. Either voice or lang must be provided.","name":"voice","in":"query"},{"schema":{"type":"string"},"required":false,"description":"BCP-47 language code (e.g. \"en-US\", \"pt-BR\", \"ja-JP\"). Required unless voice is provided.","name":"lang","in":"query"},{"schema":{"type":"string","enum":["g1","g2","g3","g5","gwn","msv","oai"]},"required":false,"description":"TTS engine code: g1, g2, g3, g5 (standard engines), gwn (Google Cloud WaveNet, BYOK), msv (Azure Speech, BYOK), oai (OpenAI TTS, BYOK). Defaults to g1.","name":"engine","in":"query"},{"schema":{"type":"string"},"required":false,"description":"System voice name for the TTS engine (e.g. \"rjs\"). Use the \"voice\" parameter instead for friendly names like \"UK English Male\".","name":"name","in":"query"},{"schema":{"type":"string","enum":["m","male","f","female"]},"required":false,"description":"Preferred voice gender: \"male\" or \"female\". Used when no specific voice name is provided.","name":"gender","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":2},"required":false,"description":"Voice pitch from 0.0 (lowest) to 2.0 (highest). 1.0 is normal.","name":"pitch","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":2},"required":false,"description":"Speech rate from 0.0 (slowest) to 2.0 (fastest). 1.0 is normal.","name":"rate","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":1},"required":false,"description":"Audio volume from 0.0 (silent) to 1.0 (full volume). Default is 1.0.","name":"volume","in":"query"},{"schema":{"type":"string","enum":["mp3","ogg","wav"]},"required":false,"description":"Output audio format. Defaults to the engine's native format (mp3 for most, ogg for g5). Supported: g1/g2/g3 → mp3 only; g5 → ogg only; gwn → mp3, ogg; msv/oai → mp3, ogg, wav.","name":"format","in":"query"}],"responses":{"200":{"description":"Audio binary data. Content-Type matches the requested format (audio/mpeg, audio/ogg, or audio/wav). Includes RV-Cached header indicating cache provenance and RV-Prosody-Applied listing the prosody knobs (pitch/rate/volume) the server applied upstream.","headers":{"RV-Prosody-Applied":{"schema":{"type":"string","description":"Comma-separated subset of {pitch, rate, volume} the server applied upstream for this call. Clients use this to decide whether to apply their own client-side fallback for the remaining knobs."},"required":true,"description":"Comma-separated subset of {pitch, rate, volume} the server applied upstream for this call. Clients use this to decide whether to apply their own client-side fallback for the remaining knobs."},"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"204":{"description":"No content — returned when volume=0 was requested; the server skips upstream synthesis entirely and emits no audio body.","headers":{"RV-Prosody-Applied":{"schema":{"type":"string","description":"Always \"volume\" for 204 responses — server skipped synthesis."},"required":true,"description":"Always \"volume\" for 204 responses — server skipped synthesis."},"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}}},"400":{"description":"Invalid synthesis request — missing required parameters or values out of range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No provider available for the requested engine code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream TTS provider returned an error during synthesis","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Synthesis"],"summary":"Synthesize text to speech (POST)","security":[{"apiKeyHeader":[],"apiSecretHeader":[]}],"description":"Converts text to speech audio via JSON request body. To enable real-time streaming, set the `Accept: text/event-stream` header — audio is streamed progressively as it is synthesized (HTTP audio streaming). Streaming requires a premium engine (gwn, msv, or oai) with BYOK credentials configured. Without the streaming header, returns complete audio as a single binary response. POST responses are not CDN-cached (Cache-Control: public, max-age=86400 only).","x-codeSamples":[{"lang":"bash","label":"cURL","source":"curl -X POST https://texttospeech.responsivevoice.org/v2/text/synthesize \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -H \"X-API-Secret: YOUR_API_SECRET\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"text\":\"Hello world\",\"voice\":\"US English Female\"}' \\\n  --output speech.mp3"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynthesizeRequest"}}}},"responses":{"200":{"description":"Audio binary data (non-streaming) or an HTTP audio stream (when Accept: text/event-stream is set). Includes RV-Cached header, X-Streaming header for streaming responses, and RV-Prosody-Applied listing knobs the server applied upstream.","headers":{"RV-Prosody-Applied":{"schema":{"type":"string","description":"Comma-separated subset of {pitch, rate, volume} the server applied upstream for this call."},"required":true,"description":"Comma-separated subset of {pitch, rate, volume} the server applied upstream for this call."},"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}},"content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"204":{"description":"No content — returned when volume=0 was requested; the server skips upstream synthesis entirely.","headers":{"RV-Prosody-Applied":{"schema":{"type":"string","description":"Always \"volume\" for 204 responses."},"required":true,"description":"Always \"volume\" for 204 responses."},"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."}}},"400":{"description":"Invalid synthesis request — missing required fields, invalid JSON, or parameter validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No provider available for the requested engine, or no streaming provider for streaming requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. The body `code` is `RATE_LIMIT_EXCEEDED` (per-minute tier limit) or `BURST_RATE_LIMIT_EXCEEDED` (per API key + client IP burst).","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer","description":"Maximum requests permitted for the authenticated key per minute."},"required":true,"description":"Maximum requests permitted for the authenticated key per minute."},"X-RateLimit-Remaining":{"schema":{"type":"integer","description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"required":true,"description":"Requests remaining in the current per-minute window; 0 once the per-minute limit is reached. Reflects the per-minute limit, not the burst check — a burst 429 may report a value above 0."},"Retry-After":{"schema":{"type":"integer","description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."},"required":true,"description":"Seconds to wait before retrying. Dynamic — varies with how far over the limit the request went; honor this header rather than assuming a fixed delay."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream TTS provider returned an error during synthesis","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{}}