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
- waitForJob threw SumeJobTimeoutError: resume polling with the job id
The Sume SDK stops waiting after 20 minutes by default, but the job keeps running. Catch SumeJobTimeoutError, read jobId and wait again. TypeScript example.
- 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 on Sume takes no bitrate_mode: what to send
The seedance-2.5 catalog row lists 'no bitrate_mode'. Control cost and size with resolution (480p to 1080p) and duration (4 to 30 s) instead.
Written by Sume