Scrapeless Wiki

What Is an Idempotent Request? – Definition and How It Works

Informational P3 what is an idempotent request

Learn which HTTP methods are idempotent, why it decides what can be retried, and how idempotency keys make POST safe.

An idempotent request produces the same server state whether it is sent once or many times. It is the property that decides whether a retry is safe.

1. Which Methods Are Idempotent

Method Idempotent Safe
GET Yes Yes
HEAD Yes Yes
OPTIONS Yes Yes
PUT Yes No
DELETE Yes No
POST No No
PATCH No No

Safe means no intended state change at all; idempotent means repeating causes no additional change. GET is both. PUT and DELETE change state but repeating changes nothing further. POST and PATCH are neither.

2. Why PUT Is and POST Is Not

PUT /items/42 with a full representation sets that item to that state. Sending it five times leaves the same result.

POST /items creates a new item. Sending it five times creates five items.

The distinction is that PUT names the target and POST asks the server to choose one. Idempotency follows from that, not from whether the method modifies anything.

3. DELETE Is Idempotent Despite the Status Code

DELETE /items/42 twice leaves item 42 deleted. The first returns 204, the second 404 β€” different responses, same resulting state.

Idempotency is a property of state, not of the response. A client that treats a 404 on a retried DELETE as a failure has misread the guarantee.

4. The Retry Rule

IDEMPOTENT = {"GET", "HEAD", "OPTIONS", "PUT", "DELETE"}

def may_retry(method, status, error):
    if method.upper() not in IDEMPOTENT:
        return False                       # unless an idempotency key is in use
    if error in {"connect_timeout", "connection_reset", "dns_failure"}:
        return True                        # request may never have arrived
    return status in {408, 429, 500, 502, 503, 504}

The hardest case is a read timeout on a POST. The request was sent, and no response arrived, so you cannot know whether the server processed it. Retrying may duplicate; not retrying may lose. Neither is correct without more information β€” which is exactly the gap idempotency keys close.

5. Idempotency Keys Make POST Retryable

A client-generated unique key on a POST lets the server deduplicate:

POST /payments
Idempotency-Key: 8f1c0d9a-3e5b-4a71-9f2e-5c7d1b0a4e63

The server stores the key with the result. A repeat of the same key returns the original result instead of acting again.

Two rules make it work: generate the key before the first attempt and reuse it across all retries, and never reuse a key for a genuinely different request.

Where an API supports this, POST becomes as retryable as PUT. Where it does not, a read timeout on a POST needs a reconciliation query β€” ask the server whether the effect occurred β€” rather than a blind retry.

6. Idempotency Is Not About the Response

A GET whose response differs each time β€” a clock endpoint, a random result, a paginated feed β€” is still idempotent, because it changes no state.

Conversely a POST that happens to return the same body each time is not idempotent if it created something each time. The property concerns effects, and confusing the two leads to unsafe retries of endpoints that looked stable.

7. In Collection Work

Almost all collection is GET, which is why retries are usually straightforward. The exceptions deserve attention:

  • POST-based search endpoints β€” technically non-idempotent but usually harmless, since they only read. Verify rather than assume.
  • GraphQL over POST β€” a query is safe; a mutation is not. The method does not distinguish them, so your retry logic must inspect the operation.
  • Session or token creation β€” non-idempotent, and retrying may invalidate the session the first attempt created.
  • Job submission APIs β€” the case where an idempotency key or a status lookup genuinely matters.

8. Summary

An idempotent request leaves the same server state however many times it is sent: GET, HEAD, OPTIONS, PUT, and DELETE qualify; POST and PATCH do not.

Retry idempotent requests freely on timeouts and 5xx; for POST, generate an idempotency key before the first attempt and reuse it across retries, or reconcile by querying the server β€” and remember that a GraphQL POST hides both a safe query and an unsafe mutation behind the same method.