https://api.echonova.aiChangelog
Changelog
The API version, what counts as a breaking change, and a dated list of every change to the v1 endpoints since their release.
This page is the record of what changed on the Developer API and when. Read it before you upgrade a client, and check it when a request that used to work starts to fail.
Version
There is one API version, v1, and it is part of every path:
/v1/text-to-speech, /v1/speech-to-text and the endpoints under them.
There is no version header. A change that cannot be made inside v1 under
the policy below ships under a new prefix, with v1 kept running for a
published period.
Breaking changes policy
A breaking change is one that makes a request that worked yesterday fail today, or changes the meaning of a response a client already reads.
| Breaking | Not breaking |
|---|---|
| Removing or renaming an endpoint, a request field, a response field or a webhook event. | Adding an endpoint. |
| Making an optional request field required. | Adding an optional request field. |
| Lowering a limit: fewer characters, shorter audio, a smaller file, fewer requests per minute. | Raising a limit. |
| Changing the type of a field or the HTTP status of a response. | Adding a field to a response or to a webhook body. |
| Changing the authentication scheme or the key format. | Adding a webhook event type. It is sent only to webhooks that subscribe to it. |
Changing an existing errorMessage string. |
Adding a new errorMessage for a new failure case. |
Two properties of the API follow from this table. First, unknown fields in a
JSON body or a query string are rejected with 400 and
property <name> should not exist, so a client must not send a field before
the entry below that introduces it. The two Speech to Text upload endpoints
read named form fields and ignore any other form field.
Second, a client should read the fields it needs from a response and ignore
the rest, because new fields will appear without notice.
Breaking changes are listed below with their date.
Changes
2026-10-23
Added:
- Both Speech to Text upload endpoints,
POST /v1/speech-to-textandPOST /v1/speech-to-text/sync, take an optionalidentify_speakersform field,trueorfalse. The default isfalse, so a request that does not send it gets the same transcript as before. A value other thantrue,false,1or0gets400withidentify_speakers must be true or false. identifySpeakers,segmentsandspeakersare now returned on the synchronous response and on the job returned by get and list.segmentsholdsstart,end,textandspeaker(spk0,spk1, … ornull);speakersholdsidandtalkTime. Jobs created before this date reportfalse,[]and[]. Thejob.completedwebhook body is unchanged.GET /v1/speech-to-text/jobs/{jobId}/exportdownloads a completed job asformat=txt,srtorjson, with speaker labels inlang=enorar. The body is the file, not the envelope. It adds the messagesformat must be one of the following values: txt, srt, json,lang must be one of the following values: en, arandJob is not completed, all400. See Speech to Text formats and output.
2026-10-21
Breaking:
- A
jobIdthat is not 24 hexadecimal characters now gets400withInvalid identifieron theGETandDELETEjob routes.DELETEanswered404for it before, and a 12-character id answered404. - A wrong HTTP method on an existing
/v1path now gets405withMethod not allowedand anAllowheader. It was404.
Changed:
- An unknown
/v1route now gets404withRoute not found. It wasCannot GET ….
Added:
- Every error on a
/v1path carriesdocs, including413and the400for a body that is not valid JSON. - The request log takes a
requestIdfilter, and the Activity Logs tab has Find by request ID. Audio processed shows seconds under a minute.
2026-10-20
Added:
- Activity and the request log now record every call to
/v1made with a known key, any method and any status.GETandDELETEcalls get rows too.GETcalls charge no credits. - A JSON body over 100 KB returns
413withRequest body is larger than 100 KB. A body that is not valid JSON returns400withRequest body is not valid JSON. Neither has an Activity row, because the key is not checked yet. - A body sent to
POST /v1/text-to-speech,POST /v1/text-to-speech/syncor a widget ticket with aContent-Typeother thanapplication/jsonreturns415withContent-Type must be application/json. - The add-endpoint dialog shows a new webhook's signing secret once, with a Copy button.
Changed:
- The per-IP limit now counts only requests the key check rejects. A valid key is never blocked by it and is limited per key only.
apiKeyNameinspending.warningandspending.exhaustedis now the key's name, not its id.
2026-10-15
Added:
- Both Speech to Text upload endpoints,
POST /v1/speech-to-textandPOST /v1/speech-to-text/sync, take an optionalnumbers_as_digitsform field,trueorfalse. The default istrue: spoken numbers come back as digits, as before. Sendfalseto keep them as the words that were said. English transcripts are never converted either way. numbersAsDigitsis now returned on the synchronous response and on the job returned by get and list. Jobs created before this date reporttrue.
2026-10-11
Added:
- Sound Cleaning:
POST /v1/sound-cleaning,GET /v1/sound-cleaning/jobsandGET /v1/sound-cleaning/jobs/{jobId}. The upload is synchronous, up to 10 minutes and 150 MB, in MP3, WAV, FLAC or OGG/Opus, and costs 1,000 credits per minute. See Sound Cleaning quickstart. cleanis a new value of a key's allowed services. A key with no service restriction can use Sound Cleaning at once. A key that lists services does not, until you Edit it in the portal and turn Sound Cleaning on; until then its Sound Cleaning calls get403.
Changed:
- Limits per request:
texton both Text to Speech endpoints is capped at 500 characters, and audio on both Speech to Text endpoints and on Sound Cleaning is capped at 10 minutes. The duration message on both Speech to Text endpoints is nowAudio exceeds the 10-minute limit. - Credits changed unit. Text to Speech costs 1 credit per character of
text, spaces and line breaks included; Speech to Text costs 100 credits per minute of audio, charged per second. See Credits. creditsConsumed,creditsUsedandcreditLimitkeep their names and types; their values are in the new unit, whole credits (no decimals). Every key's Credit Limit was multiplied by 3,000 into the new unit, so a limit of 100 now reads 300,000.- Credits are whole numbers. Each charge rounds to a whole credit (half up, at least 1), so
creditsConsumedis never a decimal; a 1 second Speech to Text call is 2 credits, not 1.666667.creditsUsedis a whole number too.
2026-10-06
Added:
- Both Speech to Text upload endpoints,
POST /v1/speech-to-textandPOST /v1/speech-to-text/sync, take an optionallanguageform field:auto,aroren. The default isauto. languageis now returned on the synchronous response and on the job returned by get and list. Jobs created before this date reportauto.
Breaking:
- A
languagevalue that was ignored before, such asen-USorarabic, now gets400withlanguage must be one of: auto, ar, en. Other extra form fields are still ignored.
2026-10-01
- Credit amounts in
/v1responses and in thespending.warningandspending.exhaustedwebhooks are now decimal numbers with up to 6 decimal places. The field names are the same:creditsConsumed,creditsUsedandcreditLimit. Parse them as floating-point numbers.percentageUsedis still an integer. - Credits are charged exactly. A charge is no longer rounded up to a whole credit and has no minimum.
- 1 credit is now $0.095 of usage, and every service is priced at its provider cost plus 20%. See Credits.
2026-09-06
Breaking:
- Both Text to Speech endpoints: the request fields
creativity,exaggerationandstabilitywere removed. A request that still sends one gets400withproperty creativity should not exist, or the same message for the other two. - Both upload endpoints: an upload over the size cap is now rejected with
413andFile too largewhile the body is being received. Before, it was rejected with400andAudio file exceeds the 50MB limit.after the whole file had been read. - Both upload endpoints: the duration rejection message changed. It was
Audio exceeds the 60-second limit. Use the async endpoint for longer audio.on the synchronous endpoint and the same text with1800-secondon the asynchronous one. It is nowAudio exceeds the 20-minute limit.andAudio exceeds the 1-hour limit.
Limits raised:
POST /v1/text-to-speech/sync:textfrom 500 to 5 000 characters. The asynchronous endpoint stays at 5 000, so both endpoints now share one cap. Earlier the same day the asynchronous cap was briefly 20 000; it is 5 000.POST /v1/speech-to-text/sync: audio duration from 1 minute to 20 minutes.POST /v1/speech-to-text: audio duration from 30 minutes to 1 hour.- Both upload endpoints: file size from 50 MB to 150 MB.
Added:
- Both Text to Speech endpoints: the optional fields
cfg(1 to 5, default 2),numSteps(16, 32, 64 or 128, default 32) andtashkeel(boolean, defaultfalse).speed(0.5 to 2, default 1) was kept. When a field is omitted the default applies, so a request without them behaves as before. - Webhooks: the
job.completedandjob.failedevents are now delivered when an asynchronous job started with an API key ends. Before this date the two events could be subscribed to but were not delivered.
2026-06-08
Breaking:
- Both upload endpoints: the duration rejection messages changed from
Audio exceeds 30 minute limit (got <seconds>s)andSync STT supports up to 60s. Use the async endpoint for longer audio.toAudio exceeds the 1800-second limit. Use the async endpoint for longer audio.andAudio exceeds the 60-second limit. Use the async endpoint for longer audio. - Both upload endpoints: a request with no file part is rejected with
File is requiredinstead ofAudio file is required.
2026-06-07
Initial release of v1:
- Text to Speech:
POST /v1/text-to-speechwith up to 5 000 characters,POST /v1/text-to-speech/syncwith up to 500 characters,GET /v1/text-to-speech/voices,GET /v1/text-to-speech/jobsandGET /v1/text-to-speech/jobs/{jobId}. - Speech to Text:
POST /v1/speech-to-textwith up to 30 minutes of audio,POST /v1/speech-to-text/syncwith up to 1 minute, both up to 50 MB,GET /v1/speech-to-text/jobsandGET /v1/speech-to-text/jobs/{jobId}. - Authentication with
Authorization: Bearerand keys starting withek_. - A rate limit of 100 requests per 60 seconds per key on each endpoint.
- Request logs in the portal.
- Webhooks with the
spending.warningandspending.exhaustedevents.
FAQ
How do I pin my client to a version?
Every path starts with /v1, and that prefix is the version. There is no
version header to send.
What happens if I keep sending a removed field?
400 with property <name> should not exist. Remove the field; the
request then behaves as it did before the field existed.
Will a new response field break my client?
Not if the client reads the fields it needs and ignores the rest. Fields are added without notice; removing or renaming one is a breaking change and is listed here.