HTTP methods carry two important properties: safe and idempotent. These aren't academic — they're the practical criteria that decide whether a request can be retried after a network failure.
Safe vs idempotent methods
A safe method is a read-only operation that doesn't change server state: GET, HEAD, OPTIONS. Because a safe request has no side effects no matter how many times it's sent, prefetching, caching, and crawling are all fine.
An idempotent method has the property that sending the same request multiple times leaves the server in the same state as sending it once. It doesn't mean the response is identical every time — it means the final server state is the same. Every safe method is automatically idempotent, and PUT and DELETE join them.
GET,HEAD,OPTIONS: safe + idempotent (no state change).PUT: idempotent.PUTreplaces a resource wholesale with a given representation, so sending the samePUTtwice yields the same final state.DELETE: idempotent. It's deleted the first time and 'already gone' after, so the final state matches (even if the second call returns 404).POST: neither safe nor idempotent — typically each call creates a new resource.PATCH: not guaranteed idempotent by the spec (it depends on how you implement it).
Why it matters for retries
Networks drop at the worst times. If the request reached the server but the response was lost on the way back, the client can't tell success from failure. With an idempotent method, it's safe to just resend — a PUT or DELETE executed twice ends in the same state. That's why HTTP clients, proxies, and load balancers conventionally auto-retry only idempotent methods. Blindly retrying a POST, by contrast, can charge a card twice or duplicate an order.
Making POST safe with idempotency keys
So how do you safely retry an inherently non-idempotent POST (a payment, an order)? The answer is an idempotency key. The client sends a unique key per request in an Idempotency-Key header; the server stores it, executes only the first request for that key, and replays the stored original response for any later request with the same key.
POST /v1/orders HTTP/1.1
Idempotency-Key: 8e2f4c9a-1b7d-4e6f-a0c3-9d2f1a7b3c4a
Content-Type: application/json
{"sku":"BOOK-01","qty":1}
The server-side essentials: (1) record the key atomically to avoid concurrent-request races, (2) store the original request and response together so retries get the same answer, and (3) expire keys sensibly. With this pattern, a client that never received a response can retry with confidence.
Designing PATCH and DELETE safely
PATCH is a partial update, so idempotency isn't guaranteed. Designed as an increment like { "balance": "+100" }, applying it twice changes the result and breaks idempotency. Designed with an absolute value like { "balance": 500 }, repeated application yields the same result and stays idempotent. To prevent concurrent-edit conflicts, send an ETag in If-Match for optimistic locking, and have the server return 412 Precondition Failed on a version mismatch.
DELETE is already idempotent, but decide as a team whether a second delete returns 404 or 204 so clients aren't confused. In short, safety and idempotency aren't mere labels — they're design principles for building resilient APIs.