Core concepts

Rate limits

Two independent limits protect the service: requests per minute per key, and jobs running at once per account.

Limits by plan

LimitInfluencerAgency
Sustained generations per minute, per account~5~10
Requests per minute, per key120240
API jobs running at once, per account1020
Active API keys520
Webhook endpoints520
Social accounts included515
Characters / products10 / 2030 / 50

Request rate

Each key has a token bucket that refills continuously at its per-minute rate, so short bursts are fine. Every response carries X-RateLimit-Limit. Over the limit you receive 429 RATE_LIMITED with a Retry-After header in seconds.

Concurrent jobs

A job is a generation that is queued or processing. When the account already has its maximum running, new image and video requests return 429 CONCURRENCY_LIMIT (Retry-After: 15) without charging. The slot frees as soon as a generation finishes, fails or is cancelled.

See your live numbers at GET /account (limits.concurrent_jobs and usage_now.active_jobs) or in Dashboard → Developers.

For batches, keep a local queue and submit a new request whenever one finishes (use webhooks), rather than submitting everything at once. Keep in flight at most limits.concurrent_jobs − usage_now.active_jobs.

Throughput

Throughput per account is set by the job limit and how long generations take:

TypicalAt busy times
Image, prompt only~60–90 sup to ~4 min
Image with character + scene~1.5–2 minup to ~5 min
Video1–6 minlonger for 12–15 s clips

So an account with N parallel jobs sustains roughly N ÷ 2 images per minute — about 5/min on Influencer and 10/min on Agency. Capacity is shared with other customers; limits are sized so everyone keeps these rates.

Need more? Email support@oriorai.com with your expected volume — higher limits are set per account, and GET /account reflects them immediately.

Handling 429s

Node.js — retry with backoff
async function withRetry(send, attempts = 5) {
  for (let attempt = 0; ; attempt++) {
    const res = await send()
    if (res.status !== 429 || attempt === attempts - 1) return res
    const wait = Number(res.headers.get('retry-after')) || 2 ** attempt
    await new Promise(resolve => setTimeout(resolve, wait * 1000))
  }
}

// Reuse the same Idempotency-Key across retries of one logical request.
const key = crypto.randomUUID()
const res = await withRetry(() => fetch(url, { method: 'POST', headers: { ...auth, 'Idempotency-Key': key }, body }))

Rate-limited responses are never stored against the Idempotency-Key, so retrying with the same key is safe.