got does not retry POST by default: enable it safely with Sume

got retries GET, PUT and DELETE but not POST. To retry a Sume paid submit, add POST to retry.methods and send one Idempotency-Key reused on every attempt.

5 min readSume
All posts

The answer

got's retry documentation says plainly that by default got does not retry on POST, and its default methods list is GET, PUT, HEAD, DELETE, OPTIONS, TRACE and QUERY. A Sume generation submit is a POST, so a dropped connection or a 502 ends in a thrown error unless you opt in.

Opting in is only safe if the server can recognise the repeat. Sume documents an Idempotency-Key request header for exactly this: reuse the same key for the same operation and payload when retrying after a timeout, and the retry returns the original job instead of billing a second one. So the rule is two settings together: retry.methods includes POST, and every attempt carries the same key.

What got retries and what it does not

The same page lists the defaults you inherit once POST is allowed. Only unsuccessful requests are retried, the default limit is 2, and the delay grows exponentially from a one second base with a little noise added.

got retry defaults relevant to a Sume submit (read 2026-10-03)
OptionDefaultWhy it matters for Sume
limit2Two extra attempts after the first
methodsGET, PUT, HEAD, DELETE, OPTIONS, TRACE, QUERYPOST is absent until you add it
statusCodes408, 413, 429, 500, 502, 503, 504, 521, 522, 524Includes 429 and the gateway timeouts
errorCodesETIMEDOUT, ECONNRESET, ECONNREFUSED, ENOTFOUND and othersCovers dropped connections

The code

This is an ES module, so top-level await works. The key is built once, outside the request, so every attempt sends the same value. Build it from a business intent such as the product and revision, not from the clock.

import got from "got";

const key = "mug-hero-2026-10-03-r1";

const res = await got
  .post("https://api.sume.com/v1/images", {
    headers: {
      authorization: `Bearer ${process.env.SUME_API_KEY}`,
      "idempotency-key": key,
    },
    json: {
      model: "sume/auto",
      prompt: "A ceramic mug on a pale oak table",
      mode: "async",
    },
    retry: { limit: 3, methods: ["POST"] },
  })
  .json();

console.log(res.data.request_id);

Two traps to avoid

First, 413 is in got's default retry list, but a payload that is too large will be too large again; Sume answers 413 payload_too_large and a retry cannot fix it. Narrow statusCodes to the ones you expect to be transient, such as 429, 502, 503 and 504, if you want to stop wasting attempts.

Second, a different payload with a reused key is a different operation. Sume says to reuse a key only for the same operation and payload, so change the key when you change the prompt. Build the key from the intent, and the retry loop becomes safe to leave on.

When the retries run out

got throws once attempts are exhausted. Do not reach for a fresh key and submit again; the job may exist. Read the job list or your stored id first, as described in Sume's jobs docs, and only submit again if nothing was created.

Checking that the retry really deduplicated

After a retry storm you want proof that only one job exists. The reference describes GET /v1/jobs as a workspace list filterable by status and type. Compare the count against your own records: one business intent should map to one job id, and the id in the final response should equal the id from any attempt that did get through.

It also helps to log your own attempt count next to the key. If the same key shows up with different job ids, something upstream changed the payload, and that is the bug to fix before you raise the retry limit.

Finally, keep the limit small. Each retry of a paid submit that did go through is free when the key matches, but each retry of a request that failed before admission is simply latency. Two or three attempts cover the transient cases without hiding a real outage.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume