OpenAI TTS API reference — audio.speech.create mapped to EasyVoice
This page is a flat parameter-by-parameter reference for developers who know OpenAI's audio.speech.create endpoint and need the equivalent reference for EasyVoice's OpenAI-compatible TTS API. Unlike the migration guide — which walks through the narrative steps of porting existing code — this page is a searchable reference: every parameter accepted by audio.speech.create, its type, whether it is required or optional, its valid values on OpenAI, and the EasyVoice equivalent behavior. It also covers the complete error-code surface — 400, 401, 403, 404, 409, 429, and 5xx — with what each code means and the correct handler for each, plus the request and response body shapes so you can validate an integration at the HTTP level without relying on any client SDK. The EasyVoice endpoint https://easyvoice.ae/api/v1/audio/speech accepts the same JSON body shape as audio.speech.create; only the base URL and the API key environment variable change.
5,000 characters per day free, no credit card. Pro $9.99/mo unlimited (fair use 10M characters per 30 days).
audio.speech.create parameters — EasyVoice equivalent table
The audio.speech.create endpoint accepts five parameters. Below is the full parameter reference with the EasyVoice equivalent behavior at https://easyvoice.ae/api/v1/audio/speech.
model (string, optional on both): OpenAI accepts 'tts-1' (standard quality) or 'tts-1-hd' (higher fidelity). EasyVoice runs Kokoro-82M as a single synthesis model and accepts any string in the model field, then ignores it: 'kokoro-82m', 'tts-1' and 'tts-1-hd' all succeed, and so does omitting the field. No 400 is returned for a model string. The stream_format and instructions fields that OpenAI's newer models accept are also accepted and ignored.
voice (string): required on OpenAI, whose tts-1 and tts-1-hd models accept nine built-in voices (alloy, ash, coral, echo, fable, onyx, nova, sage, shimmer). On EasyVoice it is optional and defaults to af_aoede. The six OpenAI voice names alloy, echo, fable, onyx, nova and shimmer work as they are: EasyVoice maps them to af_alloy, am_echo, bm_fable, am_onyx, af_nova and bf_lily. It also accepts any voice ID in its catalog (66 voices). A free API key can use the 12 free voices (for example af_aoede, af_heart, af_nova and am_michael); of the six OpenAI names only nova (af_nova) is free, and any other voice ID on a free key, including a misspelled one, returns 403 plan_required. The seven newer OpenAI names (ash, ballad, coral, sage, verse, marin, cedar) are not mapped: a Pro or pass key that sends one gets 400 invalid_voice. A cloned voice_* ID that does not exist on your account returns 404 voice_not_found.
input (string, required): The text to synthesize. OpenAI imposes a 4,096 character maximum per request. On EasyVoice a free API key can send up to 5,000 characters per request, drawn from the same 5,000-character daily pool as the web app. A Pro key can send up to 50,000 characters per request on Kokoro voices, 12,000 on Arabic voices and 2,000 on cloned voices. Longer input returns 400 (input_too_long, or input_too_long_for_sync on Arabic and cloned voices). On Pro, an oversized request to a Kokoro voice can send the header X-EasyVoice-Long-Text: async to be queued as a background job instead (see /api-docs); longer Arabic or cloned scripts go through the Jobs API. The field is required on both APIs; omitting it returns 400.
response_format (string, optional): OpenAI accepts 'mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'. EasyVoice returns mp3 (default), wav or pcm (raw 16-bit mono PCM): request mp3, wav or pcm. opus, aac and flac are accepted but the body that comes back is MP3 with HTTP 200. Default on both APIs is 'mp3'.
speed (number, optional): OpenAI accepts 0.25 to 4.0, default 1.0. EasyVoice works from 0.5 to 2.0, default 1.0; a value outside that range is clamped to it rather than rejected. For Kokoro voices the speed value is passed to the synthesis model itself rather than applied as a post-synthesis time-stretch.
Request and response shape
Request shape (identical on both APIs): HTTP POST to the endpoint with two required headers — Authorization: Bearer YOUR_API_KEY and Content-Type: application/json — and a JSON body containing the parameters described above. A minimal valid EasyVoice request body is: {"voice": "af_aoede", "input": "Hello."} (af_aoede is a free voice, so this works on a free key too). A complete request with all optional parameters: {"model": "kokoro-82m", "voice": "af_aoede", "input": "Hello from EasyVoice.", "response_format": "mp3", "speed": 1.0}.
Response shape on success (HTTP 200): Content-Type audio/mpeg for mp3, audio/wav for wav, audio/pcm for pcm. Body: raw audio bytes with no JSON envelope and no base64 encoding. response.arrayBuffer() in JavaScript (browser or Node.js), response.content in Python requests, ioutil.ReadAll(resp.Body) in Go — all return the audio bytes directly, ready to write to a file or pipe to a media player. The response does not include a JSON body, an id field, or a status field; there is no polling step. The audio is complete when the connection closes.
Response headers on success include Content-Type (as above) and X-Request-Id for distributed tracing. The response body is the complete audio file — there is no Transfer-Encoding: chunked header and no progressive streaming. All audio bytes arrive together after Kokoro finishes synthesis.
Error codes — 400, 401, 403, 404, 409, 429, and 5xx
Every EasyVoice error response carries a JSON body in the OpenAI-style envelope {"error": {"message", "type", "code", "request_id"}} plus an X-Request-Id header, so you can branch on error.code and quote the request_id to support. The codes below are read from the live /api/v1/audio/speech route.
401 Unauthorized (invalid_api_key): The Authorization header is missing, malformed, or contains an invalid or revoked API key. Common causes: incorrect copy-paste of the API key, using a staging key against production (or vice versa), or a key that has been rotated out. Fix: retrieve the correct key from the EasyVoice dashboard, set it in the appropriate environment variable, and retry. The 401 is deterministic — retrying with the same invalid key will always fail. Do not implement retry logic for 401.
400 Bad Request: input is missing (missing_input); input is longer than your plan's per-request cap (input_too_long, or input_too_long_for_sync on Arabic and cloned voices); an ev_ parameter is out of range (invalid_parameter) or the speech engine rejects the parameters (invalid_parameters); word timestamps were requested on an Arabic or cloned voice (timestamps_not_supported); or a Pro or pass key sent a voice that is neither an EasyVoice ID nor one of the six OpenAI names, such as ash or coral (invalid_voice). A speed outside 0.5-2.0 is not an error (it is clamped), and neither is a response_format of opus, aac or flac (it returns MP3 with HTTP 200). Fix: read error.code and error.message, correct the request against the parameter table above, and retry. 400 is deterministic — do not retry without fixing the request.
403 Forbidden (plan_required): a free API key asked for a voice outside the 12 free voices, or an account without Pro or an active 7-Day Pass asked for a cloned voice. Fix: use a free voice such as af_aoede, or upgrade to Pro. Deterministic — do not retry.
404 Not Found (voice_not_found): a cloned voice_* ID that does not exist on your account. Fix: check the ID against GET /v1/voices. Deterministic — do not retry.
409 Conflict (idempotency_key_reuse): only on an asynchronous long-text request that sends an Idempotency-Key header, when that key was already used with a different request body. Fix: send a new key for a different request.
429 Too Many Requests: EasyVoice uses four distinct codes. rate_limit_exceeded means the per-key requests-per-minute window is full (20 per minute on a free key, 60 on Pro), and the response carries Retry-After: 60. concurrency_limit_exceeded means a free key already has 2 requests in flight (Retry-After: 5). daily_quota_exceeded means a free key has used its 5,000-character daily pool, which is shared with the web app and resets at UTC midnight; Retry-After gives the seconds until then. fair_use_paced means a Pro account past its fair-use line sent a burst of parallel requests that would wait longer than 20 seconds for their paced turn (a single paced request waits and is then served). Pro keys have no per-key concurrency cap and no daily quota, so a Pro 429 means either the 60-per-minute window or fair_use_paced. Fix: honour the Retry-After header; for the rate and concurrency limits, back off with jitter instead of retrying in a tight loop.
5xx Server Errors: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout. These indicate transient server-side issues. Fix: implement exponential back-off with jitter (same pattern as Pro 429). 503 is the most common transient error during high server load. 504 may indicate that a very long input text is timing out the upstream synthesis — consider splitting inputs over approximately 3,000 characters into multiple requests if 504 errors occur consistently on specific payloads.
Switch to EasyVoice if you need an OpenAI-compatible API endpoint with the same request shape, where the code changes are the base URL and the API key, and the six OpenAI voice names work as they are — then add handlers for the EasyVoice-specific codes above (403 plan_required, 404 voice_not_found and the four 429 codes). Stay on OpenAI if your production error-handling logic has been tuned specifically to OpenAI's responses and you cannot absorb the testing overhead of re-validating error paths against a new endpoint.
What is the EasyVoice base URL?
EasyVoice's OpenAI-compatible TTS endpoint is https://easyvoice.ae/api/v1/audio/speech. This is the only URL change required when switching from OpenAI's TTS API at the HTTP level. The path segment /audio/speech is identical to OpenAI's path structure, so URL-building code in your TTS service module changes only the base domain — not the path, not the query string, not the header format.
For HTTP libraries with a configurable base URL, set baseURL or base_url to 'https://easyvoice.ae/api/v1'. For direct HTTP calls, replace 'https://api.openai.com/v1/audio/speech' with 'https://easyvoice.ae/api/v1/audio/speech'. The Authorization header format (Bearer token), the Content-Type (application/json) and the response format (raw audio bytes) are the same on both endpoints. Errors follow the same HTTP status conventions, with EasyVoice-specific additions — 403 plan_required, 404 voice_not_found and four distinct 429 codes — covered in the error section above.
Code samples
Real working code, not pseudo-code. Every request below assumes you've set EASYVOICE_API_KEY and OPENAI_API_KEY as env vars where shown.
Request and response — curl
Full request/response shape against the EasyVoice OpenAI-compatible endpoint# Minimal valid request (voice + input required)
curl -X POST https://easyvoice.ae/api/v1/audio/speech \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"voice":"af_aoede","input":"EasyVoice TTS API — parameter reference."}' \
--output out.mp3
# Response: HTTP 200, Content-Type: audio/mpeg, body = raw MP3 bytes (no JSON wrapper)
# af_aoede is a free voice; Pro voices such as af_alloy return 403 on a free key
# Full request with all optional parameters
curl -X POST https://easyvoice.ae/api/v1/audio/speech \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
"voice": "af_aoede",
"input": "EasyVoice TTS API — parameter reference.",
"response_format": "mp3",
"speed": 1.0
}' \
--output out.mp3
# Error codes: 400 bad param | 401 bad key | 403 plan_required | 404 voice_not_found
# 429 rate/concurrency/daily quota (honour Retry-After) | 5xx serverVoices to try
Every voice below is callable via the same voice parameter — preview audio samples and read the full character profile.
Frequently asked questions
Which audio.speech.create params does EasyVoice support?▾
EasyVoice supports all five audio.speech.create parameters: model (any string is accepted and ignored), voice (an EasyVoice catalog ID or one of the six OpenAI names), input (text string, required), response_format (mp3, wav or pcm) and speed (0.5–2.0, values outside are clamped; OpenAI allows 0.25–4.0). A free key is limited to the 12 free voices; of the six OpenAI names only nova is free. The request body shape is the same as OpenAI's: change the base URL and the API key, and the six OpenAI voice names work as they are.
What does a 429 from the EasyVoice TTS API mean?▾
EasyVoice returns 429 with one of four error codes. rate_limit_exceeded: the per-key window is full (20 requests per minute on a free key, 60 on Pro), Retry-After: 60. concurrency_limit_exceeded: a free key already has 2 requests in flight, Retry-After: 5. daily_quota_exceeded: a free key has used its 5,000-character daily pool (shared with the web app), which resets at UTC midnight. fair_use_paced: a Pro account past its fair-use line sent a burst of parallel requests that would wait longer than 20 seconds for their paced turn. Pro has no concurrency cap and no daily quota, so a Pro 429 means either the per-minute window or fair_use_paced. Fix: wait for the Retry-After header before retrying, and send paced requests one at a time.
Is response_format the same as OpenAI's on EasyVoice?▾
Request mp3, wav or pcm. EasyVoice returns mp3 (default), wav or pcm (raw 16-bit mono PCM). opus, aac and flac are accepted but the body that comes back is MP3 with HTTP 200, not a 400 error. Default is mp3 on both APIs. For most production integrations, mp3 and wav cover all playback and processing use cases.
What is the EasyVoice base URL for OpenAI-compatible requests?▾
https://easyvoice.ae/api/v1/audio/speech — the only change from OpenAI's api.openai.com/v1/audio/speech is the base domain. The path /audio/speech is identical. For libraries with a configurable baseURL, set it to https://easyvoice.ae/api/v1. Authorization header format, Content-Type, and response handling are unchanged.
How does EasyVoice handle the model parameter?▾
EasyVoice accepts any string in the model field and ignores it: sending model: 'tts-1', 'tts-1-hd' or 'kokoro-82m' (or anything else) succeeds with no error. Omitting the model parameter also succeeds. This drop-in compatibility means existing OpenAI-shaped code with the model field set works on EasyVoice without any defensive parameter removal.
Related OpenAI migration guides
Migrate from OpenAI TTS to EasyVoice: change the base URL and key
Keep the official OpenAI SDK: change the base URL and API key. The six OpenAI voice names work as they are (nova is free; the other five need Pro).
OpenAI TTS quickstart — first audio in 5 steps, no credit card
OpenAI TTS quickstart alternative. EasyVoice: 5 steps, no credit card, first audio in under 2 minutes. Account, API key, curl request, mp3 playback — free tier.
OpenAI TTS alternative — handling the complete audio file response
OpenAI TTS alternative for developers handling audio responses. EasyVoice returns a complete audio file — fetch with arrayBuffer() or res.content, write once received. $9.99 flat.
OpenAI is removing tts-1, tts-1-hd and the gpt-4o-mini-tts snapshots on 6 January 2027
OpenAI removes tts-1, tts-1-hd and two gpt-4o-mini-tts snapshots on 6 January 2027. Switch the speech call to EasyVoice: change the base URL and key.
Vendor comparison: EasyVoice vs OpenAI TTS
Side-by-side feature comparison covering voices, languages, pricing tiers, free limits, API surface, and the why-people-look / where-each-wins breakdown.
API reference: /audio/speech, Jobs API and limits
Every request parameter, rate limit and error code, the Jobs API for long text, and copy-paste examples using the official openai SDK with the EasyVoice base URL.
Start migrating off OpenAI TTS today
5,000 characters per day free, no credit card. Pro $9.99/mo unlimited replaces per-character bills once they grow past the breakeven shown on the pricing page.