Rate limits
Limits apply per organization and are shared by all of its projects and keys. A request over a limit gets HTTP 429 with a Retry-After header; nothing is created or held.
Default limits
| Limit | Default | Counts |
|---|---|---|
| Generation creates | 120 per minute, bursts of up to 30 | Create a generation, and estimates that must measure an input video |
| Generation reads | 60 per second | Get and list generations, and other estimates |
| Open tasks | 100 | Tasks that are processing and have not started running yet |
| Running at once: video | 3 | Unfinished video tasks |
| Running at once: image | 10 | Unfinished image tasks |
| Running at once: audio | 10 | Unfinished audio tasks |
The create limit is a token bucket: it holds up to 30 tokens and refills at 2 per second (120 per minute). Each create takes one token, so a burst of 30 is accepted at once and later creates are paced at the refill rate.
A task counts toward its media type's limit from acceptance until it closes, whether it succeeds, fails or is canceled. The current limits and how many tasks are running are shown on the API keys page in the console.
When you hit a limit
Every limited request gets HTTP 429 with a Retry-After header in seconds and an error body. Nothing is recorded and no Credits are held, so it is safe to send the same request again after the wait.
HTTP/1.1 429 Too Many Requests
Retry-After: 5
Content-Type: application/json
{
"error": {
"code": "concurrency_limited",
"message": "organization video concurrency limit reached; retry when a running task finishes",
"retryable": true,
"request_id": "9f2c41d07ab3e5c8a1d40be7c26f5e13"
}
}error.code | Cause | Retry-After |
|---|---|---|
concurrency_limited | As many tasks of this media type are unfinished as the limit allows. | 5 seconds |
rate_limited | The create or read rate is used up, or the open-task backlog is full. | Seconds until a token is available, at least 1 |
注意
A 429 is the only create error you may retry automatically, because the request was refused before a task existed. For a timeout or a dropped connection, read Interrupted submissions first.
Retry with backoff
Wait at least the Retry-After value, then send the request again. Cap the number of attempts and add jitter so many workers do not retry together.
Python
import random, time, requests
def create(session, body, attempts=5):
for _ in range(attempts):
res = session.post("https://api.vizoapi.com/v1/generations", json=body, timeout=60)
if res.status_code != 429:
return res
wait = int(res.headers.get("Retry-After", "1"))
time.sleep(wait + random.uniform(0, 1))
return resNode.js
export async function create(body, attempts = 5) {
let res;
for (let i = 0; i < attempts; i++) {
res = await fetch("https://api.vizoapi.com/v1/generations", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VIZO_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (res.status !== 429) return res;
const wait = Number(res.headers.get("Retry-After") ?? "1");
await new Promise((r) => setTimeout(r, (wait + Math.random()) * 1000));
}
return res;
}Queue on your side
To run more tasks than the concurrency limit, keep your own queue and submit the next task when one closes. A webhook tells you the moment a task finishes, so you do not have to poll for free slots.
Poll a running task every 5 to 10 seconds at most. Reads share the per-second limit across your organization.
Raise a limit
Ask for a higher limit from the API keys page in the console. Tell us the media type, the expected volume and the peak number of tasks you run at once.