API & Automation·6 min read·

HTTP Methods and Idempotency in Practice

GET, PUT, DELETE, and POST each carry a different retry guarantee. Use the method contract plus idempotency keys so clients retry safely.

NB

Netbay Engineering

Netbay Engineering

On this page

The HTTP method you choose is a retry contract, whether you think about it or not. A client that times out is going to send the request again, and the method you picked decides whether that second request is harmless or doubles an order. This post walks the method semantics you need to get right and then adds the mechanism that makes even POST safe to retry: the idempotency key.

The method contract

  • GET and HEAD are safe and idempotent. They must have no side effects, and applying them repeatedly changes nothing. Clients, caches, and browsers are all allowed — even expected — to repeat them freely.
  • PUT is idempotent: it replaces the resource at a known address. Sending the same PUT twice lands on the same final state, so re-sending after a timeout is safe.
  • DELETE is idempotent by default. Deleting something that is already gone is still success; return the same 204 or 404 consistently.
  • POST is neither safe nor idempotent. It is the method for creating things, and repeating it creates duplicates. This is the method your timeouts worry about.
  • PATCH is not idempotent by default because partial updates can be order-sensitive and type-sensitive; if you want the retry guarantee, define your patch semantics carefully or move the operation to PUT.

The practical consequence: GET for reads, PUT for full replacements, DELETE for removals, POST for creation, and idempotency keys when POST becomes the only honest option.

Making POST safe to retry

Creation is the tricky case. The client generates a unique token per logical operation — a per-purchase key, a per-VM key — and sends it in the Idempotency-Key header. The server records the key together with the first response it produced. If the same key arrives again, the server replays the stored response instead of performing the operation a second time. The client can now retry without fear: the worst case is you receive the same answer you already had.

A few details make this work in production. The server should store the exact status code and body of the first response and replay them byte-for-byte, so retries look identical to a first-try client. Keys need an expiry, because storing them forever trades one leak for another. And when two requests arrive with the same key while the first is still processing, respond with 409 or 425 Too Early rather than running the operation twice in parallel.

What the client and server each own

  • Client: generate the key once per logical operation, keep it stable across retries, and reuse it with the exact same request body.
  • Server: store key, status, and body with a TTL; dedupe concurrent duplicates; replay cached responses; and never apply the same key to a different payload.
javascript
// Client side: one key per logical purchase, reused on every retry
async function purchase(body) {
  const res = await fetch("https://api.example.in/v1/purchases", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID()
    },
    body: JSON.stringify(body)
  });
  return res;
}

// Safe to call it again on a timeout or a 5xx: no duplicate charge.
const first = await purchase({ plan: "ubuntu-4gb", period: "monthly" });
bash
# Inspect the request headers you actually sent; -i shows the response
curl -s -i -X POST https://api.example.in/v1/purchases   -H "Idempotency-Key: 7f2d9c1e-4a3b-42c1-9d0e-11b2c3d4e5f6"   -H "Content-Type: application/json"   -d '{"plan":"ubuntu-4gb","period":"monthly"}'
idempotency key dedupe on retry POST /purchases Idempotency-Key: K first execution store K → response R retry, same key K replay response R POST + key ⇒ at-most-once in practice · no duplicate VM, no double charge

Takeaway

Method semantics tell clients when retrying is safe; idempotency keys make retrying safe where the method cannot. Pick the right verb for the job, add a key to everything non-idempotent, and your API's worst network failures stop being your clients' data-quality problems.

The pattern matters even on small automation: when you orchestrate VM lifecycles through an API like the one at netbayhosts.in, an idempotent provisioning call is the difference between one server and an invoice full of accidents.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance