Schedule the next Ideogram 4.5 batch wave from ratelimit-reset
Size each wave from ratelimit-remaining and wave_size_hint, and when the write bucket is empty sleep ratelimit-reset seconds. A pure function you can test.

A batch of Ideogram 4.5 images runs into two ceilings. The request budget says how many POSTs fit in this minute; generation admission says how many jobs Sume will accept right now. The admission docs expose generation_limits.wave_size_hint, defined as max(1, floor(queue_capacity_remaining x 0.75)), and the rate limit headers expose ratelimit-remaining and ratelimit-reset.
Take the smallest of the two, submit that many, and when the answer is zero, sleep until the window resets.
The wave function
Read the headers from your last submit, since the headers describe the budget that request spent from, and a submit spends the write bucket.
def next_wave(headers: dict, wave_size_hint: int, pending: int):
"""Return (how many to submit now, seconds to wait before the next try)."""
remaining = int(headers["ratelimit-remaining"])
reset = int(headers["ratelimit-reset"])
size = min(remaining, wave_size_hint, pending)
return (size, 0) if size > 0 else (0, reset + 1)
print(next_wave({"ratelimit-remaining": "118", "ratelimit-reset": "41"}, 4, 40)) # (4, 0)
print(next_wave({"ratelimit-remaining": "0", "ratelimit-reset": "41"}, 4, 40)) # (0, 42)
print(next_wave({"ratelimit-remaining": "9", "ratelimit-reset": "41"}, 4, 2)) # (2, 0)Headers used
| Header | Meaning | Used for |
|---|---|---|
| ratelimit-remaining | Requests left in the window | Cap on this wave |
| ratelimit-reset | Seconds until the window resets | Sleep when remaining is 0 |
| retry-after | Seconds to wait, sent on 429 | Overrides your schedule |
Rules for the loop
- A
429names the exhausted budget inerror.details.scope(readorwrite); only pause submits for awritescope. 429 queue_fullis a capacity answer, not a rate limit. The refused job releases its idempotency key, so replay the same key later.- Add one second past the reset so you do not land on the boundary.
- Keep reading
ratelimit-limitfrom responses instead of hard-coding a plan table.
Sources
Related posts
More in Developers
- SDK waitForJob after a 202 from createImage: TypeScript sample
When createImage returns 202 on a slow gpt-image-2.5 render, pass the job id to waitForJob from @sume-com/sdk and read the terminal job instead of hand-polling.
- Rotate the Sume webhook secret twice in 24 hours: the oldest one dies
One rotation keeps the old secret valid for 24 hours. A second rotation inside that window retires the secret from two rotations ago. Verifier in Python.
- Seedance 2.5 job failed: refund, new idempotency key, and rerun cost
A failed Seedance 2.5 job is refunded on Sume. Retry with a new Idempotency-Key; the old one replays the failed job. A Python handler and the rerun cost.
- Low-latency TTS without streaming: one job per sentence
Sume TTS has no streaming. To start playback early, split the script by sentence, submit the jobs in parallel and play each file as it finishes, in order.
Written by Sume