Skip to main content

Rate limits

Generation endpoints are rate limited. Reads are not.

How it works​

Limits are counted per user, not per IP — every key you own draws on the same allowance, so splitting work across keys does not buy you more throughput.

Exceeding a limit returns 429 with a Retry-After header giving the number of seconds to wait:

HTTP/1.1 429 Too Many Requests
Retry-After: 3600

POST /gen/video is additionally subject to a daily video cap. That cap surfaces the same way, so a 429 there may mean "wait an hour" or "you're done for today" depending on which limit you hit — Retry-After tells you which.

What the limits are​

Limits vary by plan, and they change when plans are retuned. Rather than print numbers here that would quietly go stale, see botlobby.ai/pricing for the current figures.

GET /subscription/current also returns your own limits under limits and your consumption under usage_summary, which is the authoritative answer for your account at this moment.

Backing off​

Honour Retry-After. Exponential backoff on top of it protects you when many workers hit the wall together:

import time
import requests

def post_with_backoff(url, json, headers, max_attempts=5):
delay = 1
for attempt in range(max_attempts):
response = requests.post(url, json=json, headers=headers, timeout=30)

if response.status_code != 429:
return response

# Retry-After is authoritative; the exponential delay is only a floor
# for the case where it is absent.
wait = int(response.headers.get("Retry-After", delay))
time.sleep(wait)
delay = min(delay * 2, 60)

raise RuntimeError(f"still rate limited after {max_attempts} attempts")

Do not retry in a tight loop without sleeping. It will not get you through the limit any faster and makes the 429 last longer.

Designing around limits​

  • Check before a batch. GET /subscription/current tells you your remaining headroom before you start, which is better than discovering it partway through.
  • Queue rather than parallelise. Since limits are per user, ten concurrent workers hit the same ceiling as one — they just hit it in a messier way.
  • Separate reads from writes. Polling task-status is not rate limited, so a polling loop never eats into your generation allowance.