MAI Flash 429 and spend limits vs Sume queued jobs and idempotency
OpenRouter's MAI-Voice-2.1-Flash returns 429 on rate limits and has spend limits. Sume accepts jobs into queued status. See an idempotent double submit.

Flash on OpenRouter answers 429 when you hit rate limits and enforces per-account spend limits, so a batch client has to back off and retry. Sume also returns 429 (rate_limited on submit volume, queue_full when the queue is full), but below those limits a submit is accepted and sits in queued until a worker is free, and repeating a submit with the same Idempotency-Key returns the original job.
Two failure models
The OpenRouter page for the Flash model lists 429 for rate limits and per-account spend limits. Sume's job envelope returns data.request_id, a status URL and a result URL; queued is a normal accepted state, and concurrency applies when workers move jobs to processing. A full queue returns 429 queue_full, so back off there too.
| Situation | MAI-Voice-2.1-Flash via OpenRouter | Sume TTS Router |
|---|---|---|
| Rate limit | 429 response | 429 rate_limited over the limit; under it, accepted and queued until processed |
| Spend cap | Account spend limits apply | Account balance and credits apply |
| Retry after a lost response | Client must avoid a duplicate | Same Idempotency-Key returns the same job |
| Delivery | Synchronous audio response | Poll status, then read result |
Submit twice, pay once
The script submits the same request twice with one key. The second response has data.idempotency_hit set, and both carry the same request id.
for i in 1 2; do
curl -sS -X POST https://api.sume.com/v1/tts-router/generate \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: demo-double-submit-001" \
-d '{"model":"sonic-3.6","transcript":"One line, one charge.","avatar_handle":"product_host","language":"en"}' \
| jq -c '.data | {request_id, idempotency_hit}'
donePolling rules
Read GET /v1/jobs/:id/status with exponential backoff, then /result when it completes. Never resubmit a paid request that is only queued; the key makes a retry safe but a new key makes a new charge.
Limits
I did not exercise Flash's 429 path. Check the retry headers on the response you receive before you write a back-off policy.
Sources
Related posts
More in Developers
- MAI Flash defaults to PCM: wrap it as WAV, vs Sume output formats
OpenRouter lists MAI-Voice-2.1-Flash output as mp3 or pcm, default pcm, which will not play as saved. Wrap it as WAV in Python; Sume defaults to mp3.
- MAI-Voice 24 kHz 160 kbps mp3 header vs Sume output_format settings
Microsoft's REST example asks for audio-24khz-160kbitrate-mono-mp3. Sume has no 160 kbps option: mp3 bit rates are 32, 64, 96, 128 or 192 kbps at up to 48 kHz.
- MAI voice ids end in a model name; Sume voice ids do not
A MAI id like en-US-Harper:MAI-Voice-2.1-Flash is not a Sume voice id. Sume takes a UUID or voi_ plus 32 hex and returns 400 invalid_voice_id for anything else.
- Make a Talking Photo Speak Spanish: TTS Language, Then Fabric
Two steps on Sume: generate Spanish speech with the language set, then animate a still with Fabric. Costs for a 30-second clip and what Avatar Video can't do.
Written by Sume