Back to HTTP Methods
Q1Question 1 of 5

What are idempotent methods, and which HTTP methods are idempotent (and why)

Key Concepts
  • An idempotent method produces the same server-side state whether called once or many times.
  • GET, HEAD, OPTIONS, PUT, and DELETE are idempotent; POST and PATCH are generally not.
  • Idempotency is defined by server state, not the response body, so a DELETE returning 404 on retry is still idempotent.
  • POST is not idempotent because each call typically creates a new resource, risking duplicates on retry.
  • The Idempotency-Key header retrofits idempotency onto POST by storing and returning the result of the first call.
  • Proxies and retry libraries rely on idempotency to decide whether to automatically retry a failed request.

Definition

An HTTP method is idempotent when making the same request once or many times leaves the server in the same final state, so it is safe to retry.
  • Idempotent – sending the identical request one time or many times produces the same server-side state; the response may differ, but the effect is identical.
  • GET, HEAD, OPTIONS – idempotent because they only read and never change state.
  • PUT – idempotent because it fully replaces a resource; the second write overwrites with the same values.
  • DELETE – idempotent because the resource ends up deleted whether you delete it once or ten times.
  • POST – not idempotent because each call typically creates a new resource, so two identical POSTs create two resources.
  • PATCH – not guaranteed idempotent because a relative change like "add 100 to balance" stacks on repeats.

Imagine Rajesh presses an elevator button at National Bank of India. Pressing it once or ten times calls the elevator to the same floor: that is idempotent. Now imagine a "transfer 50,000 rupees" button. Pressing it ten times moves 500,000 rupees: that is not idempotent and it is dangerous. Idempotent actions are the ones where repeating the press changes nothing extra, so if Rajesh is not sure whether the first press registered, he can press again without fear.

Understanding the Concepts

What is idempotency?

Idempotency is a property of an HTTP method where performing the same request multiple times produces the same server-side state as performing it once. The key word is server state, not the response body. A DELETE that returns 204 the first time and 404 the second is still idempotent because the server state is identical: the resource is gone.

JAVA
// Idempotency is about server state, not response.
// DELETE /api/v1/beneficiaries/BEN-77
// First call:  deletes BEN-77, returns 204 No Content
// Second call: BEN-77 already gone, returns 404 Not Found
// Server state after both: BEN-77 does not exist → IDEMPOTENT

Why does idempotency matter?

Idempotency matters because networks are unreliable. Rajesh's mobile app sends a request, the response times out, and the app does not know whether the server processed it. For an idempotent method, the app can safely resend without causing harm. For a non-idempotent method like POST, resending risks a duplicate side effect, like creating two transfers and moving money twice.

Proxies, load balancers, and retry libraries all rely on idempotency to decide whether to retry. A reverse proxy may automatically retry a GET or PUT on a 503, but it must not retry a POST.

JAVA
// Network timeout scenario at National Bank of India:
// Rajesh sends PUT /api/v1/beneficiaries/BEN-77 with full body.
// Timeout – did the server process it?
// Safe to retry: PUT is idempotent, so resending replaces with the same values.

// Rajesh sends POST /api/v1/transfers with amount 50000.
// Timeout – did the server process it?
// NOT safe to retry: POST is not idempotent, resending may create a second transfer.

Which methods are idempotent and why?

GET is idempotent because it only reads. Calling GET /accounts/SAV2001 a hundred times returns the same data and changes nothing.

HEAD is idempotent for the same reason as GET: it reads headers only and never mutates state.

OPTIONS is idempotent because it only reports which methods are allowed.

PUT is idempotent because it is a full replacement. Sending the same complete body twice ends in the same state both times.

DELETE is idempotent because the end state is the same: the resource does not exist.

DIAGRAM
| Method | Idempotent | Safe | Reason |
|---|---|---|---|
| GET | Yes | Yes | Read-only, no state change |
| HEAD | Yes | Yes | Read-only, no body |
| OPTIONS | Yes | Yes | Read-only, reports allowed methods |
| PUT | Yes | No | Full replacement, same body → same state |
| DELETE | Yes | No | Resource ends deleted either way |
| POST | No | No | Each call creates a new resource |
| PATCH | No | No | Relative changes stack on repeats |
JAVA
package com.bank.controller;

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;

@RestController
@RequestMapping("/api/v1/beneficiaries")
public class BeneficiaryController {

    // PUT is idempotent: sending the same body twice gives the same result.
    @PutMapping("/{id}")
    public Beneficiary replace(@PathVariable String id,
                               @RequestBody BeneficiaryRequest body) {
        return beneficiaryService.replace(id, body);
    }

    // DELETE is idempotent: deleting twice leaves the resource gone.
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable String id) {
        beneficiaryService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

Why is POST not idempotent?

POST typically creates a new resource each time. Two identical POST /transfers calls create two transfers and move money twice. This is the dangerous case in banking.

JAVA
// Two identical POSTs create two transfers – not idempotent.
// POST /api/v1/transfers  {"from": "SAV2001", "to": "SAV2002", "amount": 50000}
// → 201 Created, transfer TXN-001
// POST /api/v1/transfers  {"from": "SAV2001", "to": "SAV2002", "amount": 50000}
// → 201 Created, transfer TXN-002  ← DUPLICATE, money moved twice!

Why is PATCH not guaranteed idempotent?

PATCH is not guaranteed idempotent because the specification does not require it. A PATCH that says "add 100 to the balance" runs twice and adds 200. However, a PATCH that sets an absolute value, like "set nickname to Landlord," happens to be idempotent. Because the specification does not require it, you must treat PATCH as non-idempotent unless you designed it otherwise.

JAVA
// Relative PATCH: not idempotent.
// PATCH /api/v1/accounts/SAV2001  {"addToBalance": 100}
// Run once:  balance goes from 1000 to 1100
// Run twice: balance goes from 1100 to 1200 ← NOT the same state

// Absolute PATCH: happens to be idempotent.
// PATCH /api/v1/beneficiaries/BEN-77  {"nickname": "Landlord"}
// Run once:  nickname becomes "Landlord"
// Run twice: nickname is already "Landlord" ← same state

How do you make POST safe to retry with an Idempotency-Key?

The industry solution for making POST safe to retry is an Idempotency-Key header. The client generates a unique key per logical operation and sends it with the request. The server records the key and the result. If the same key arrives again, the server returns the stored result instead of processing a new operation.

JAVA
package com.bank.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;

@RestController
@RequestMapping("/api/v1/transfers")
public class TransferController {

    @PostMapping
    public ResponseEntity<TransferResponse> create(
            @RequestHeader("Idempotency-Key") String key,
            @Valid @RequestBody TransferRequest req) {
        // If key already processed, return stored result – no new transfer.
        return ResponseEntity.ok(transferService.createOnce(key, req));
    }
}
DIAGRAM
# Idempotency-Key flow:
# First call:
#   POST /transfers  Idempotency-Key: abc-123  → creates TXN-001, stores result under abc-123
# Retry (same key):
#   POST /transfers  Idempotency-Key: abc-123  → finds abc-123, returns stored TXN-001
# Result: money moves exactly once.

What are common mistakes with idempotency?

A common mistake is assuming all PATCHes are idempotent. Another is implementing DELETE as non-idempotent by throwing an error when the resource is already gone, instead of treating it as a no-op. A third is not adding an Idempotency-Key to POST endpoints that have side effects.

JAVA
// Wrong: DELETE throws on already-deleted resource – breaks idempotency.
@DeleteMapping("/beneficiaries/{id}")
public void delete(@PathVariable String id) {
    if (!repo.existsById(id)) {
        throw new NotFoundException("Already deleted"); // Breaks idempotency!
    }
    repo.deleteById(id);
}

// Correct: DELETE is a no-op if already gone – idempotent.
@DeleteMapping("/beneficiaries/{id}")
public ResponseEntity<Void> delete(@PathVariable String id) {
    repo.deleteById(id); // If not found, no-op or ignore
    return ResponseEntity.noContent().build(); // Always 204
}

What should a senior engineer know about idempotency?

A senior engineer knows that idempotency is a server-state property that intermediaries rely on for retry decisions. They know that payment APIs like Stripe and Razorpay require Idempotency-Key headers on POST because moving money twice is unacceptable. They also know that the idempotency key must be stored atomically with the operation result, usually in the same database transaction, to avoid race conditions where two retries arrive simultaneously.

Visual Overview

DIAGRAM
┌────────────────────────────────────────────────────┐
│              Idempotency Decision Flow               │
├────────────────────────────────────────────────────┤
│                                                    │
│  Client sends request ──▶ Network timeout          │
│                           │                        │
│                    Did it succeed?                  │
│                    Unknown!                         │
│                           │                        │
│              ┌────────────┴────────────┐           │
│              ▼                         ▼           │
│     Idempotent method?          Non-idempotent?    │
│     (GET, PUT, DELETE)          (POST, PATCH)      │
│              │                         │           │
│              ▼                         ▼           │
│     Safe to retry              Need Idempotency    │
│     automatically              Key to retry safely │
│                                                    │
└────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────┐
│           Idempotency-Key Flow for POST             │
├────────────────────────────────────────────────────┤
│                                                    │
│  Client generates key: "abc-123"                   │
│       │                                            │
│       ▼                                            │
│  POST /transfers  Idempotency-Key: abc-123         │
│       │                                            │
│       ▼                                            │
│  Server: key "abc-123" seen before?                │
│       │                    │                       │
│      No                   Yes                      │
│       │                    │                       │
│       ▼                    ▼                       │
│  Process transfer     Return stored result         │
│  Store result under   (no new transfer)            │
│  key "abc-123"                                     │
│       │                    │                       │
│       ▼                    ▼                       │
│  Return TXN-001       Return TXN-001               │
│  (money moves once)   (money moves once)           │
└────────────────────────────────────────────────────┘

Key Takeaway

An HTTP method is idempotent when sending the same request one time or many times leaves the server in the same final state. The important detail is that idempotency is about server state, not the response, so a DELETE that returns 204 the first time and 404 the second is still idempotent because the resource ends deleted either way. The idempotent methods are GET, HEAD, OPTIONS, PUT, and DELETE, while POST and PATCH are generally not idempotent.

The reasoning behind each is straightforward. GET, HEAD, and OPTIONS only read, so repeating them changes nothing. PUT is a full replacement, so writing the same complete representation twice ends in the same state. DELETE leaves the resource gone whether called once or ten times. POST is not idempotent because it normally creates a new resource each time, and PATCH is not idempotent in general because a PATCH like "add one hundred to the balance" would stack up on repeats.

The reason this matters in a banking system is retries under unreliable networks. At National Bank of India, when Rajesh tapped Transfer on a weak signal and the response timed out, his app could not tell whether the money had moved. If the transfer endpoint were a plain POST, that retry would have created a second transfer and moved fifty thousand rupees twice. Because POST is not idempotent by the specification, the team added an idempotency key: the client sends a unique key in a header, the server records it, and a repeat of the same key returns the original transfer instead of executing a new one.

In practice, idempotent methods like GET, PUT, and DELETE can be retried freely by intermediaries and background jobs, while POST endpoints that move money or have side effects need an Idempotency-Key to become safely retryable. That combination keeps the system both resilient to network failures and correct about never double-charging a customer.

Remember one sentence: idempotency means the same request produces the same server state no matter how many times you send it, and it is what makes retries safe.

---

Share
Use ← → arrow keys to navigate