What Is Idempotency?
An operation is idempotent if performing it multiple times produces the same result as performing it once. This property is what makes retries safe and distributed systems predictable.
Published May 8, 2026The word comes from mathematics: a function f is idempotent if f(f(x)) = f(x) for all x. In computing, idempotency means that running the same operation once or ten times leaves the system in the same state. It's a property you design in, not one that emerges automatically.
Why idempotency matters
Networks fail. Requests time out. Clients retry. In distributed systems, the question is not whether an operation will be retried, but whether retrying will cause problems. An idempotent operation can be retried safely. A non-idempotent one may cause double charges, duplicate records, or corrupted state.
Idempotency in HTTP
The HTTP specification defines which methods are idempotent:
- GET — idempotent and safe (no side effects). Retrieve the same data every time.
- HEAD — idempotent and safe. Same as GET but headers only.
- PUT — idempotent. Replacing a resource with the same data twice leaves the resource unchanged on the second call.
- DELETE — idempotent. Deleting a resource that's already gone should return 404, not an error that breaks a retry loop. The state (resource doesn't exist) is the same after the first and subsequent calls.
- POST — not idempotent. Submitting the same order form twice creates two orders.
- PATCH — depends on implementation.
PATCH {"amount": 50}(set amount to 50) is idempotent.PATCH {"amount": "+10"}(increment by 10) is not.
Browsers and HTTP clients exploit this: they'll automatically retry safe, idempotent requests but not POST requests, because it's safe to retry GET but not safe to retry a form submission.
Making POST idempotent with idempotency keys
Payment APIs (Stripe, Adyen, etc.) let you attach an idempotency key to a request — a unique string you generate (usually a UUID) that identifies this specific operation:
POST /v1/charges
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 2000,
"currency": "usd",
"customer": "cus_xyz"
}
If the request times out and you retry with the same key, the server checks whether it already processed this key. If yes, it returns the original result without executing the charge again. Your customer is charged exactly once, even if you retried three times.
Implementing idempotency keys in your own API is a deliberate design choice: you need storage to track keys, TTL logic to expire old ones, and a locking mechanism to prevent two concurrent requests with the same key from both executing.
Idempotency in database operations
INSERT vs UPSERT. A plain INSERT fails or creates a duplicate if run twice. An INSERT ... ON CONFLICT DO UPDATE (upsert) is idempotent: it sets the record to the desired state regardless of whether it existed before.
-- Not idempotent: second run creates a duplicate or errors
INSERT INTO users (id, email) VALUES (42, '[email protected]');
-- Idempotent: second run updates if exists, does nothing harmful
INSERT INTO users (id, email) VALUES (42, '[email protected]')
ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email;
Absolute vs relative updates. UPDATE accounts SET balance = 100 is idempotent. UPDATE accounts SET balance = balance + 10 is not. The first sets a known state; the second applies a change that accumulates on retry.
Idempotency in message queues and event processing
Message queues (RabbitMQ, Kafka, SQS) guarantee at-least-once delivery in most configurations. A consumer may receive the same message twice. If the processing is not idempotent — for example, it sends a welcome email for each received event — duplicate processing causes real harm.
The standard solution is deduplication: track message IDs that have been processed (in a database or cache with a TTL) and skip messages you've already handled.
def handle_event(event):
key = f"processed:{event['id']}"
if redis.exists(key):
return # already handled
send_welcome_email(event['user_id'])
redis.setex(key, 3600 * 24, "1") # remember for 24 hours
The test for idempotency
Ask: "If I run this operation a second time with identical inputs, does the system end up in the same state as after the first run?" If yes, it's idempotent. If no — if the second run creates extra records, charges money again, or produces a different result — it isn't.
Designing for idempotency is not always simple, but the investment pays dividends in reliability. Systems that can safely retry operations without human intervention handle failures gracefully. Systems that can't require manual intervention every time something goes wrong in the network layer.