ky retry on POST for Sume: Idempotency-Key, 40 s timeout, v2 baseUrl
ky does not retry POST by default and times out at 10 s, but Sume sync can hold 30 s. A tested ky v2 config with a stable Idempotency-Key and no 429 retries.

Configure ky with methods: ["post"] in retry, raise timeout above its 10-second default, and send an Idempotency-Key that you made before the first attempt. The ky README (read 2026-10-10) says retries default to a limit of 2, exclude POST, cover status codes 408, 413, 429, 500, 502, 503 and 504, and honor Retry-After.
Those defaults are fine for a GET. For a paid Sume submit, two defaults need changing, and ky v2 renamed an option that older Sume-and-ky snippets still use.
The tested config
I ran this against a local server that answered 503 and watched ky retry three times with the same key. Running ky 2.2.0, the old prefixUrl option threw The prefixUrl option has been renamed prefix in v2; the sample uses baseUrl, with a path that has no leading slash.
import ky from "ky";
const api = ky.create({
baseUrl: "https://api.sume.com",
headers: { "x-api-key": process.env.SUME_API_KEY! },
timeout: 40_000, // ky defaults to 10 s per attempt; sync mode can hold 30 s
retry: {
limit: 3,
methods: ["post"], // POST is not retried by default
statusCodes: [408, 500, 502, 503, 504], // not 413, not 429 queue_full
},
});
// One key per intent, created before the first attempt and reused by retries.
const idempotencyKey = `promo-8823-v1`;
const res = await api
.post("v1/image-1.0/generate", {
headers: { "Idempotency-Key": idempotencyKey },
json: { prompt: "Matte black bottle on marble", mode: "async" },
})
.json<{ data: { request_id: string } }>();
console.log(res.data.request_id);The three changes and why
| ky default | Problem with Sume | Change |
|---|---|---|
| POST not retried | A dropped response after the job was created leaves you guessing | Allow post, but only with a stable Idempotency-Key |
| timeout 10,000 ms | Sync and subscribe waits can hold up to 30 s | Use 40,000 ms, or use async mode and poll |
| Retries 413 and 429 | 413 is payload_too_large and a queue_full 429 is not fixed by waiting | Drop 413 and 429, or inspect error.code first |
Why 429 needs a look before you retry
Sume has two kinds of 429. rate_limited is a request-volume limit with retry-after and ratelimit-reset headers, and retrying after the wait is correct. queue_full means the workspace already holds its accepted job capacity, so another attempt a second later fails the same way. The errors page separates them by error.code.
A ky afterResponse hook can read the body, branch on error.code, and throw for queue_full so the retry never happens. That is better than listing 429 and hoping.
Key hygiene
ky builds the retry from the same options object, so a header you set once is sent on every attempt. That is exactly what you want from the idempotency key, and exactly what you do not want if you generate the value inside a beforeRequest hook, which runs again for each retry. Build the key outside the request and pass it in.
Log the request_id from the error envelope on the final failure so support can trace the attempt on the API side.
Sources
Related posts
More in Developers
- Launch-week 503 provider_capacity_exceeded: safe video submit retries
New video models cause capacity spikes. Retry 429 and 503 on Sume with the same Idempotency-Key, honor retry-after, never retry 402. Python sample included.
- Let a browser poll your backend, not the Sume API: a proxy pattern
Keep SUME_API_KEY on the server: submit async, return the job id, and give the browser a read-only status route. A 27-line TypeScript route with the checks.
- Lint a Format package in CI before you PUT it
A short Python check for a Sume Format package: SKILL.md name equals the slug, allowed folders and extensions, file names, 100 MiB limits. Run it before PUT.
- Lip-sync a 2-minute monologue: split one TTS wav into H3 Max clips
MiniMax H3 Max lip-sync takes audio of 5 to 14.8 seconds. For a longer speech, render one TTS wav and cut it at sentence ends; the cost is worked below.
Written by Sume