Computer Science

What Is Idempotency, and Why It Makes Double-Clicking Pay Safe

Idempotency means producing the same result when the same request is sent multiple times. This covers why it matters with network failures and retries, the difference between GET, PUT, DELETE, and POST, and how to implement an Idempotency-Key.

5 min read
Cover image for What Is Idempotency, and Why It Makes Double-Clicking Pay Safe

You press the payment button, but the screen stays frozen for a while. It makes you nervous. You want to press it again.

But won’t pressing it twice charge you twice?

Not in a well-designed system. The property that guarantees this has a name.

Idempotency.

It appears throughout backend documentation, HTTP specifications, and payment API guides, but its definition fits in one line. The difficult questions are “Why does it matter so much?” and “How do you implement it?”

This article answers both.

Here is the key summary.

  1. Idempotency: the property that produces the same result whether a request is sent once or many times
  2. Why it matters: networks fail, failures require retries, and retries are safe only when operations are idempotent
  3. In HTTP, GET, PUT, and DELETE are idempotent; POST is not
  4. The practical mechanism that makes POST safe is the Idempotency-Key

Definition: Doing It Multiple Times Is Like Doing It Once

Idempotency is a term from mathematics. If an operation f satisfies f(f(x)) = f(x), it is idempotent.

The absolute value function is a good example. |−5| = 5 and |5| = 5.

The result is the same whether you apply it once or a hundred times.

Applied to an API, it looks like this.

The server state is the same whether the same request is sent once or N times.

Note: the response does not have to be the same; server state does. Even if the second DELETE request returns 404, it is idempotent because the server state—“the resource does not exist”—is unchanged.

An everyday analogy is an elevator button. Press the fifth-floor button five times, and the elevator still goes to the fifth floor once.

If it were a “go up five more floors” button, the result would change every time. The first is idempotent; the second is non-idempotent.


Why It Matters: Networks Inevitably Fail

The importance of idempotency can be explained with a single scenario.

The client sends a payment request. The server processes the payment.

But the network goes down while the response is on its way back.

The client has no way to know whether the request failed before reaching the server (no payment) or only the response was lost after processing (payment made).

A timeout looks exactly the same in both cases.

Sequence diagram showing a payment processed only once when retried with the same idempotency key
When the response is lost, you cannot tell whether the payment succeeded. That is why idempotency is needed.

There are only two choices: give up on the retry (the payment may not have happened) or retry (creating a duplicate if it did). Both are terrible.

Idempotency removes this dilemma. If a request is idempotent, “send it again if unsure” is always safe.

Automatic retries, at-least-once delivery in message queues, and repeated client refreshes are no longer something to fear.

That is why distributed-systems design documents repeatedly say, “Retry only idempotent operations.”


Idempotency by HTTP Method

The HTTP specification (RFC 9110) defines idempotency for each method.

Method Idempotent? Reason
GET O Reading data does not change state
PUT O “Replace with this value” produces the same value every time
DELETE O “Delete it” leaves it deleted every time
POST X “Create a new one” adds one each time it is called
PATCH Conditional “Set this field to X” is idempotent; “add 1” is non-idempotent

The contrast between PUT and POST is fundamental. PUT specifies the “resulting state,” so it is idempotent; POST specifies an “action,” so it is non-idempotent.

PATCH depends on what the operation does.

When a browser returns to a POST page with the Back button and warns, “Resubmit this form?”, it is because the browser knows resending POST is unsafe.


The Practical Mechanism: Idempotency Keys

But signing up, creating an order, and making a payment are inherently POST operations. You cannot avoid non-idempotent work.

That is why a mechanism for making non-idempotent requests idempotent was introduced: the Idempotency-Key.

Its behavior is simple.

  1. For each request, the client generates a unique key (usually a UUID) and sends it in a header
  2. If the key is new, the server processes the request normally and stores the response with the key
  3. If the same key arrives again, the server skips processing and returns the stored response unchanged

Whether it is a retry or a double-click, the server processes it only once as long as the same key is used. Payment APIs such as Stripe and Toss Payments support this header and cover it as a required item in their integration guides.

One client-side detail: “Use the same key when retrying.”

If you generate a new key for every retry, the server sees each one as a different request. Create a key per “request with the same intent.”

Payment terminal processing only one of two envelopes marked with the same key and issuing one receipt
When the same idempotency key arrives, the server processes it once and returns the stored response.

Summary

  • Idempotency means the server state remains the same no matter how many times the same request is sent (f(f(x)) = f(x))
  • When a network failure leaves you unsure whether an operation completed, retries are safe only if it is idempotent
  • HTTP: GET, PUT, and DELETE are idempotent; POST is non-idempotent; PATCH depends on its contents
  • PUT is idempotent because it specifies the resulting state; POST is non-idempotent because it specifies an action
  • Make non-idempotent POST idempotent with an Idempotency-Key — the same key is processed only once
  • Always reuse the same key when retrying. A key represents a “request with the same intent”