Base URL https://api.echonova.ai

Developers

Rate limits

How many requests one API key can make per minute, the headers that report the count, what a 429 looks like, and how to back off.

The API limits how many requests one key can make to one endpoint per 60 seconds. This page gives the numbers, the headers that report where you stand, and the exact 429 response so you can handle it in code.

Limit

Fact Value
Requests allowed 100
Window 60 seconds. Each request counts against the limit for 60 seconds after it is made.
Scope One API key on one endpoint. Two keys have separate counts, and one key's calls to /v1/text-to-speech do not count against its calls to /v1/speech-to-text/jobs.
Block Once a key exceeds 100 requests on an endpoint, that endpoint rejects the key for 60 seconds. Rejected requests are not counted. When the block ends the count starts again from zero.
Applies to All fifteen /v1 endpoints.

POST /v1/sound-cleaning is also bounded by its upload, not only by the request count: audio of 10 minutes or less, 150 MB or less, in MP3, WAV, FLAC or OGG/Opus. See Sound Cleaning quickstart.

The count is per key, not per workspace. If several systems share a workspace, give each its own key so a busy one does not block the others.

Response headers

An accepted request returns three headers that tell you where the key stands on that endpoint. A rejected request returns Retry-After instead.

Header When Meaning
X-RateLimit-Limit Accepted request The limit, 100.
X-RateLimit-Remaining Accepted request Requests left in the window, 0 at the lowest.
X-RateLimit-Reset Accepted request Seconds until the window resets.
Retry-After Rejected request Seconds until the block ends.

The 429 response

The rejected request gets HTTP 429 with this body:

{
  "data": null,
  "statusCode": 429,
  "success": false,
  "errorMessage": "ThrottlerException: Too Many Requests"
}

A different 429 exists. When a call would take the key past its credit limit, the body says Monthly spending limit exhausted and no Retry-After header is sent. Waiting does not clear it; see Credits. Check errorMessage before you retry.

What to do

  • Read Retry-After, wait that many seconds, then retry. Do not retry sooner; requests made during the block are rejected and the block does not shorten.
  • If you retry on other errors as well, use exponential backoff with jitter so a burst of failures does not turn into a burst of requests.
  • Watch X-RateLimit-Remaining and slow down before it reaches 0.
  • Use one key per system. The limit is per key, so separate keys give separate windows.

Stay under the limit while waiting for jobs

Polling is the usual way to hit the limit. Every poll is a request against the job endpoint, whatever the jobId, so let the result come to you where you can.

Recommended

  • A webhook on job.completed and job.failed, which costs no requests, with one poll after 15 minutes of silence as a fallback.

Less effective

  • A loop that polls /v1/text-to-speech/jobs/{jobId} every 500 milliseconds for every job in flight.

If you must poll, use a fixed interval of at least 3 seconds for Text to Speech and 5 seconds for Speech to Text. See Retry requests safely for the loop.

A second limit of the same size, 100 requests per 60 seconds per endpoint, is counted per client IP address. It counts only requests that the API key check rejects: a missing or invalid key (401), a key with no access to the service (403), or a workspace plan that is inactive or not found (403). A request with a valid key is never blocked by it. Valid keys are limited per key only. A blocked IP gets the 429 body above with Retry-After, even when it sends a bad key.

FAQ

Is the limit per workspace or per key?

Per key, and per endpoint. Two keys in one workspace have separate counts, and one key's calls to /v1/text-to-speech do not count against its calls to /v1/speech-to-text/jobs.

Do rejected requests extend the block?

No. Rejected requests are not counted, and the block ends when Retry-After says it does. They do not shorten it either.

Do polls count?

Yes. Every GET on a job endpoint is a request against that endpoint's limit.

How do I tell this 429 from the spending-limit 429?

By errorMessage. This one says ThrottlerException: Too Many Requests and carries Retry-After; the spending one says Monthly spending limit exhausted and carries no header.