Rate Limits
Requests per minute, per day and at once, text length, and how to stay within them
Rate limits keep the API fast and reliable for everyone. Every API key has the same limits, whatever your plan: the API is open on every plan, Free included, and only your API credit sets how much you can generate.
Limits
| Limit | Every API key |
|---|---|
| Requests per minute | 600 |
| Requests per day | 1,000 |
| Requests at once | 20 |
| Words per voice-over, tone directions included | 5,000 |
| Requests per minute from one IP address | 600 |
| Request body | 25 MB |
Your key's limits are also on the Developer Dashboard, and in the key.rateLimit field of GET /me.
What counts
A request counts toward your requests per minute and per day once it succeeds. Requests that fail, whatever the reason, don't count. Only voice-overs count toward your requests at once, from the moment Voice213 accepts them until they end, whether they succeed or fail.
| Endpoint | Checked against the per-minute and per-day limits | Counts toward them | Counts toward requests at once |
|---|---|---|---|
POST /tts | Yes | Yes, when it succeeds | Yes, while it runs |
GET /me | Yes | No | No |
POST /tts/estimate, GET /models, GET /tts/voices, GET /tts/tones | No | No | No |
Requests per minute
The minute starts with the first request that counts. Once your key has made 600 requests within that minute, further requests get 429 until 60 seconds have passed since it started:
{
"error": "Rate limit exceeded: RPM limit exceeded",
"limit": 600
}This 429 has no Retry-After header: wait until the minute is over, then retry.
Requests per day
Once your key has made 1,000 requests in a day, further requests get 429 until the day ends, at midnight server time:
{
"error": "Rate limit exceeded: Daily limit exceeded",
"limit": 1000
}Don't retry this one in a loop: it won't clear until the next day. If you regularly need more, contact us.
Requests at once
One key can have up to 20 voice-overs (POST /tts) running at the same time. A voice-over runs from the moment Voice213 accepts it until its audio is ready or it fails. Once 20 are running, the next one gets 429:
{
"error": "Rate limit exceeded: Concurrent request limit exceeded",
"limit": 20
}- A voice-over your client cancels or stops waiting for keeps running until Voice213 finishes it (see When your client gives up), and keeps its place until then. A quick retry after a timeout can therefore hit this limit.
- Estimates,
GET /meand the catalogue endpoints don't count, and are never refused by it. - This
429has noRetry-Afterheader and doesn't count toward your per-minute or per-day limits. Retry once one of your running voice-overs has ended.
A queue with a fixed number of workers, like the example below, keeps you under it.
Requests per IP address
The whole API also accepts up to 600 requests a minute from one IP address. Every response under /api carries the standard headers for this limit:
RateLimit-Policy: 600;w=60
RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 42Past it, requests get 429 with {"error": "RATE_LIMITED"} and a Retry-After header giving the seconds to wait.
Text length
- Words per request: up to 5,000 in
POST /tts, in both formats, and in Estimate Cost. Every word counts, tone directions included. Longer texts get400 Max 5000 words exceeded, with thewordsyou sent and themaxWordsallowed. Long texts also take longer to generate, so split very long scripts. - Something to speak: a text made only of tone directions has nothing to speak, and gets
400 EMPTY_TEXT. - Unspoken text: tone directions, spaces and invisible characters stay free, but they can't outweigh the spoken text. A text may be at most twice its billable characters (counted as at least 100) plus 1,000 characters long, every character counted. Longer texts get
400 TEXT_TOO_LONG, with thecharsyou sent and themaxCharsallowed. See Too much unspoken text. - Request body: up to 25 MB, for every endpoint.
Staying within the limits
Run requests through a queue, and space them out rather than sending bursts:
import asyncio
import os
import httpx
MAX_PARALLEL = 5 # how many requests you run at once (20 at most)
semaphore = asyncio.Semaphore(MAX_PARALLEL)
async def synthesize(client, text):
async with semaphore:
for attempt in range(5):
response = await client.post(
"https://api.voice213.com/api/v1/tts",
headers={"Authorization": f"Bearer {os.environ['VOICE213_API_KEY']}"},
json={"text": text, "model": "voice213-lite", "voice": "nariman"},
)
if response.status_code != 429:
response.raise_for_status()
return response.json()
if "Daily limit" in response.json().get("error", ""):
response.raise_for_status() # won't clear today
await asyncio.sleep(float(response.headers.get("Retry-After", 15 * (attempt + 1))))
response.raise_for_status()
async def main(texts):
async with httpx.AsyncClient(timeout=300) as client:
return await asyncio.gather(*(synthesize(client, t) for t in texts))- Combine short texts into one request where it makes sense. One request of 1,000 words counts once; twenty requests of 50 words count twenty times. Every request is also billed at least 100 characters, so very short requests cost more per character.
- Save audio you generate more than once instead of generating it again.
- Read the free catalogue endpoints (
/models,/tts/voices,/tts/tones) once and keep the result: they rarely change.
Was this page helpful?