Software

What Is Idempotency? Stop Duplicate Requests in Your API

Talha Aslan 20 min read 2 views

What is idempotency?

Idempotency is the property that running an operation once or many times leaves the system in the same final state. In API terms, a request that you resend after a network failure does not create a second order, a second charge, or a second record.

In short, idempotency makes repetition harmless. Connections drop, responses get lost, and clients retry because they cannot know what happened. A reliable API therefore assumes from day one that the same request may arrive twice.

In this guide, we explain the term at a conceptual level. You will not see a code block here, only the logic, the use cases, and the limits.

What is idempotency in plain terms, using an elevator button?

Picture the button for the third floor of an elevator. First, you press it once and the elevator heads to floor three. Then you get impatient and press it five more times. Nothing changes. The elevator still goes to one floor, and the outcome stays the same. That button is idempotent.

Now imagine telling a newsstand clerk, "Give me one newspaper." If the clerk thinks you did not speak and you repeat the order, you walk away with two newspapers. That action is not idempotent, because every repetition changes the result.

In practice, software lives between these two examples. The command "set the order status to confirmed" behaves like the elevator button. By contrast, the command "add one item to the cart" behaves like the newspaper order. However, with the right design you can turn the second into the first, and we will show how later.

The idea also comes from mathematics. Applying a function twice gives the same result as applying it once, as with an absolute value: the absolute value of minus three is three, no matter how often you take it. So what is idempotency in software? It is the same promise applied to server operations.

Why do double charges happen in the first place?

The root cause is the moment when the client cannot see the server's response. A customer clicks Pay. Next, the server receives the payment and processes it. Then the response gets lost on the way, or the timeout expires. So the client assumes the payment failed.

At that point, three things can happen:

  • The application resends the request automatically.
  • Or the customer refreshes the page or taps the button again.
  • A gateway or load balancer in the middle repeats the request on its own.

In all three cases, the server sees the same request twice. If the server cannot tell it is a repeat, it charges the customer twice. Double charging is therefore rarely a random bug. It is the natural result of a gap in the design.

The danger is that nobody notices. Meanwhile, the customer sees one payment, while the server sees two. That difference surfaces only during month-end reconciliation or a complaint. The problem grows silently.

Also, server error pages create similar situations. For example, a client that receives a 502 Bad Gateway error cannot know whether the operation actually happened.

Which HTTP methods are idempotent?

Fortunately, the HTTP standard gives a clear framework. IETF RFC 9110 defines an idempotent method as one where multiple identical requests have the same intended effect as a single request. According to the standard, GET, HEAD, OPTIONS, and TRACE are idempotent, and so are PUT and DELETE. However, POST and PATCH are not idempotent by definition.

One detail matters here. Note that the standard talks about the intended effect. A log file may gain extra lines, and a second DELETE may return a "not found" response. The final state on the server is still the same in both cases: the record is gone.

The standard also guides retries. A client may automatically repeat an idempotent request when the connection fails. For a method that is not idempotent, the client should not retry automatically unless it knows the request is idempotent in practice. You can read the details in the idempotent methods section of RFC 9110.

This knowledge helps in two places. First, you choose the right method when you design an endpoint. Second, you decide which requests a client or gateway may safely retry. Browsers, gateways, and client libraries often behave according to this distinction, so a wrong method choice can trigger automatic repeats where you do not want them.

What is idempotency for PUT, PATCH, and POST in REST design?

These three methods cause the most confusion in REST style APIs. First, PUT replaces a resource with the content you send. Sending the same PUT five times leaves the resource in the same state five times, so it is idempotent.

Then PATCH handles partial updates, and its result depends on how you write the request. A PATCH that says "set the name to Alex" works idempotently in practice. A PATCH that says "increase the counter by one" gives a different result on every repetition. For this reason the standard does not treat PATCH as idempotent and leaves the decision to the designer.

POST usually means "create something new" or "start an action." A repetition therefore creates another record. Payments, orders, and message sending are mostly POST flows, which is why idempotency keys matter most there.

As a practical rule, design the operation as "write the target state" whenever possible, similar to PUT. If that is not possible, add a key to your POST.

How do safe, idempotent, and atomic operations differ?

People mix up these terms often, so a short comparison helps. All three relate to reliability, yet each answers a different question.

ConceptQuestion it answersExampleWatch out
------------
SafeDoes the request change server state?Reading a product pageEvery safe method is idempotent, but not the other way around
IdempotentDoes repetition change the outcome?Setting an order status to confirmedState may change once, and repeats add no further effect
AtomicDoes the operation fully happen or not at all?A bank transfer: debit one account and credit anotherIt does not prevent repeats, it prevents half finished work
DeterministicDoes the same input give the same output?A tax calculation functionIt says nothing about side effects

In short, atomicity solves the risk of partial completion, while idempotency solves the risk of repetition. Taken together, a reliable payment system often needs both. If you want to see how transactions fit into the wider server side picture, read our guide on what back end development is.

How does an idempotency key work?

For operations that are not naturally idempotent, the fix is to attach an identity to the request. First, the client generates a unique key for each logical operation and sends it along. When the server sees the key for the first time, it performs the operation and stores the result next to the key. If the same key arrives again, the server skips the work and returns the stored result.

Stripe's official documentation describes this approach well. According to it, the provider saves the status code and body of the first request made with a key, and later requests with the same key receive that saved response. You can read the details in the Stripe documentation on idempotent requests.

The critical point is that the key identifies the intent, not the request. After all, the customer said "pay" once. It does not matter how many times that intent travels over the network, because the result must be single.

Let us walk through an example scenario. A customer confirms a cart, and the app sends "complete the order" with a key. The server creates the order, but the response is slow. Then the app retries with the same key. Because the key is known, the server returns a summary of the first order instead of creating a second one. As a result, the customer sees one order, and your team issues one invoice.

The IETF HTTP API working group is also working on a standard `Idempotency-Key` header for this purpose. Because it is still a draft, check its current status on the IETF draft page.

How should a client generate and reuse keys?

The quality of the key decides how reliable the mechanism is. When you generate keys, follow these rules:

  • Use a random value with a very low chance of collision, such as a UUID.
  • Never derive the key from personal data, because an email address or phone number can leak.
  • Reuse the same key for every repeat of the same logical operation.
  • Then create a new key when the user starts a new operation.
  • Generate and store the key before the operation starts, so you can continue with it even if the app crashes.

The last point is the one teams skip most often. If you regenerate the key on every retry, the server treats each request as new, and the protection stops working entirely.

Providers also set limits such as key length and retention time. Check the current values in your provider's official documentation, because we do not quote numbers here.

On mobile apps, however, the situation is trickier. The user can send the app to the background, the connection can change, and the operating system can resend the request later. If your app stores the key locally, it can resume the same operation with the same key after a restart. As a result, the user does not start the same payment twice.

How do you implement idempotency on the server side?

The core idea is simple: record the key before you perform the operation, and attach the result once the operation ends. A few details make it harder in practice.

First, the key record and the business logic must sit inside the same transaction boundary. Otherwise, the payment goes through but the record fails, or the reverse happens. A uniqueness constraint in the database helps here, because a second attempt to insert the same key gets rejected.

Second, think about concurrent requests. If two requests with the same key arrive at the same moment, one should proceed while the other waits or receives an "in progress" response. Without a lock, both requests may believe they are the first.

Third, decide what to store, because storage choices shape later behavior. Storing the result for successful calls is the minimum. For failures, store consistent outcomes, but skip requests that failed validation before any work began, because the user should be able to fix the input and try again.

There is also the question of scope. Will you evaluate the key per user or per endpoint? Usually the user and the operation type together form the scope. That way, two different users who accidentally generate the same key do not affect each other.

What should happen when the same key arrives with different content?

This is the corner of the design that people overlook most. For instance, imagine a client sends the same key with a different amount. What should you do? Returning the result of the first operation would mislead the client, since it may think the second amount went through.

Robust systems store a fingerprint of the request content together with the key. If a new request does not match the first request for that key, the server returns a clear error. The Stripe documentation states that its idempotency layer compares incoming parameters with the original request and returns an error when they differ.

Therefore, this behavior does two jobs. It helps developers catch accidental key reuse early. It also prevents a faulty or malicious client from pushing a new operation through with an old key.

What response should the server give to a repeated request?

When the server meets a known key a second time, it is in one of three situations. So each one needs a different message for the client.

  • If the first request has finished, return the stored response exactly. For the client, nothing should look different.
  • If the first request is still running, return a response that means "in progress," so the client can ask again shortly.
  • If the key is the same but the content differs, report the conflict openly.

What matters is that the client can understand what happened, because guessing causes bugs. Silently creating a new record, or returning a vague error, forces developers to guess.

It also helps to mark the response. A header that says the response came from a stored result makes support work easier. Which status code you choose depends on your API contract and your framework, so consult RFC 9110 for the meaning of each code.

Finally, tell clients how long a stored response stays valid. If your documentation explains the duration and behavior clearly, integrating teams do not need to guess. Clear documentation is often the cheapest way to prevent errors.

How do network timeouts and retries work with idempotency?

First, a retry is the automatic resending of a request that seems to have failed. Alone, it is dangerous, because you cannot know whether the first request actually succeeded. Also, idempotency removes that danger. Together they deliver "send at least once, process at most once."

So what is idempotency doing in a retry loop? It makes the loop safe to run. Without it, you risk either lost data or double processing, and you can have neither retries nor correctness for free.

A good retry strategy has these parts:

  • Use the same idempotency key on every attempt.
  • Wait longer after each failure, which is called exponential backoff.
  • Add a small random offset, called jitter, so thousands of clients do not hit the server at once.
  • Set a maximum number of attempts.
  • Retry only temporary failures, such as timeouts and dropped connections.

For permanent failures like missing permissions, retrying only adds load. Telling the two apart is therefore important.

Does disabling the button stop double clicks well enough?

No. Disabling a button after the first click is a good habit, but it only reduces repeats in the interface. It does not stop network layer retries, a mobile app resending in the background, or a user opening a second tab.

Instead, think about two layers together. On the interface, lock the button and show a "processing" message. On the server, provide the real protection with idempotency. The interface improves the experience, while the server protects the data.

Do not forget page refreshes either. A user who reloads the browser after submitting a form may resend the same request. Redirecting the user to a separate result page after a successful action lowers that risk, although it never replaces server protection.

Why does idempotency matter for payment and order APIs?

First of all, moving money is the hardest action to undo. That is why payment providers highlight idempotency keys in their official documents. Order systems carry similar risks: double orders, double stock deductions, and double invoices.

Consider a small online shop as an example scenario. A customer pays by card and finishes the 3D Secure step, but the mobile network drops and the page never receives the answer. Then the customer tries again. Without idempotency, two charges appear, followed by a refund, a support thread, and lost trust. With idempotency, the second request receives the stored result and the customer sees one payment.

When you choose a payment provider, check whether it supports this feature. Our article on how to choose a payment gateway covers the general criteria. For problems in the verification step, see what we recommend when 3D Secure authentication fails.

A shared language inside the team helps as well. For a product owner, the short answer to "what is idempotency" is this: one customer intent produces one result. A developer turns that sentence into a technical requirement, and the test team turns it into an acceptance scenario.

What is idempotency for events and webhooks?

Message queues and webhooks mostly work with "at least once" delivery, so duplicates are normal. In other words, the same event can reach you twice. Treating that as a bug is a mistake, because the system repeats on purpose. Losing an event is worse than repeating it.

For this reason, the receiving side must be idempotent. This pattern is called an idempotent consumer. The approach is simple: every event has a unique identifier, the consumer records the identifiers it has processed, and it ignores an event it has already seen.

For example, if a payment provider sends a "payment completed" notification twice, you must not move the order to "ready for shipping" twice. We cover event based systems in our article on event driven architecture, including event identity and ordering.

Meanwhile, ordering is a separate matter. Events do not always arrive in the order they were sent, so the consumer must not let an old event overwrite a newer state. Checking a timestamp or a version number reduces that risk, and it complements idempotency.

How do you design operations that are naturally idempotent?

However, you do not always need to store keys. Sometimes you can design the operation to be idempotent from the start. The trick is to phrase the command as a target state instead of a change.

DesignIdempotent?Why
---------
"Increase the balance by 50"NoEvery repeat adds another increase
"Set the balance to 150"YesA repeat writes the same target value
"Add one item to the cart"NoEvery repeat raises the quantity
"Set the item quantity to 2"YesThe target quantity is fixed
"Create a new record"NoEvery repeat generates a new identifier
"Create or update the record with this identifier"YesThe identifier is fixed, so one record remains

This approach requires the client to generate the identifier. If the server generates it, the first request and the repeat receive different identifiers. A small design choice therefore creates a large safety net.

Also, some people call this the target state model. Repeating such commands needs no extra protection. Of course not every job fits this shape, because withdrawing money from an account is incremental by nature. In that case, you return to the key method.

How does idempotency help in microservices and distributed workflows?

In systems where several services work together, calls run as a chain. For instance, the order service calls the stock service, and the stock service calls the shipping service. If one service times out in the middle, it becomes unclear which steps finished.

In that uncertainty, the safest path is to make every step in the chain idempotent. The coordinating service can then resend the step it is unsure about. If the step already completed, nothing changes. If it did not, it completes now.

Compensating actions must follow the same principle. Running a refund step twice must not mean two refunds. That is why refund requests carry their own keys.

For the broader context of service design, you can read our Python versus Go backend comparison. That article focuses on language choice, so it does not cover idempotency, and this guide fills the gap.

What are the limits and risks of idempotency?

Idempotency does not solve every problem. You should know these limits:

  • Side effects: A key does not automatically protect effects that reach external systems, such as emails or notifications. Tie those effects to the same key.
  • Retention: Key records do not live forever. After the period ends, a repeat may count as a new request.
  • Storage cost: Saving a response for each key needs extra space and maintenance.
  • False confidence: Developers may neglect timeouts and error handling because idempotency exists.
  • Semantic traps: With PUT you say "replace everything," and if two clients send different values at the same moment, the last writer wins. That is idempotent but still unwanted.

So idempotency is a safety net, not a replacement for correct business logic. In addition, if stored records contain personal data, review retention with your own legal advisor. This article is not legal advice.

Knowing these limits sets the right expectation. For a manager who asks what is idempotency, the honest answer is this: it is a design principle that limits the damage of repetition in a world where networks and clients are unreliable. It is not a product that removes errors by magic.

Which mistakes appear most often when teams add idempotency?

The mistakes we see again and again come from small details. Watch for these:

  • Regenerating the key on every retry.
  • Writing the key record in a separate transaction from the business logic.
  • Storing only successful responses and never storing failures.
  • Skipping the comparison between the request content and the key.
  • Deriving the key from a counter that anyone could guess.
  • Failing to record a unique event identifier in webhook handlers.
  • Leaving emails and notifications that go to outside systems unprotected.

Each item looks minor alone. Together they appear as double charges, double invoices, or repeated notifications. For that reason, we suggest making the checklist part of your code reviews.

How do you test an idempotent API?

Testing proves that the mechanism really works. You can try these scenarios in order:

  1. Send the same request twice with the same key and confirm that the second response matches the first.
  2. Check that only one record appears in the result table.
  3. Send different content with the same key and confirm that the server returns a clear error.
  4. Send two requests with the same key at the same time and see that only one is processed.
  5. Cut the connection on purpose in the middle of the operation, then retry with the same key.
  6. Watch the behavior after the retention period ends.

If something goes wrong, you need to inspect logs. Our guide on debugging techniques for developers helps at this stage. To spot repeated requests in your server access logs, you can also try our log file analyzer.

Avoid real money in test environments. Prefer the test modes that payment providers offer, so you can try repeat scenarios safely. Moreover, if you add these tests to your continuous integration pipeline, you notice immediately when a change breaks the protection.

What is idempotency in serverless systems, and why does it matter more there?

Cloud functions may be retried automatically by the platform. Even if you do nothing, a function can run twice with the same input. For this reason, writing idempotent code is almost mandatory in serverless architecture.

We covered cold starts and cost in our article on what serverless is. Here we add just one point: when a function times out, the platform cannot know whether the work finished, so it retries. Your function must tolerate that.

Marketing integrations behave the same way. Repeated events in conversion tracking lead to double counting. In our article on Meta Pixel and Conversions API duplicate events, you can see the same logic at work as deduplication with an event identifier.

What is a practical checklist for developers and businesses?

The following list works for both the technical team and the product owner:

  1. List every POST flow that creates money, stock, or records.
  2. Choose an idempotency key or a naturally idempotent design for each flow.
  3. Generate the key in the client before the operation starts, and keep it the same across attempts.
  4. Write the key record inside the same transaction boundary as the business logic.
  5. Return a clear error when the same key arrives with different content.
  6. Add a lock or a uniqueness constraint against two simultaneous requests.
  7. Record the event identifier in webhook and queue consumers.
  8. Use exponential backoff and a limited number of attempts for retries.
  9. Document the retention period and the privacy requirements.
  10. Turn the test scenarios above into automated tests.

On the business side, ask providers whether their documentation supports idempotency. That question is one of the most valuable ones before you sign a contract.

Do not fill in this list once and forget it. Review it whenever you add an endpoint or switch providers. Idempotency is less a feature you build once and more a habit you maintain with every change.

When should you get expert help?

For a small internal tool, you may not need every safeguard above. However, if you take payments, manage stock, or integrate with third party systems, idempotency belongs at the start of the design. Adding it later costs far more than cleaning up duplicates in existing data.

Start by looking at the risk. If a mistake means charging customers, losing stock, or creating a legal duty, build the protection first. If the effects are small and reversible, a lighter solution may be enough. Answering what is idempotency for your own workflows is the healthiest way to decide.

At Talha Aslan and team, we support integration and API design through our custom software development service. Mapping the risks of an existing system is part of that work.

Finally, remember that standards and provider documents change over time. The concepts here are lasting, but for heading names, limits, and details, always check the official documentation.

Frequently Asked Questions

Is idempotency the same as an idempotent API?
Idempotency is the name of a property, while an idempotent API is an interface that provides it. In other words, an idempotent API gives the same result when it receives the same request many times. It achieves this either through the nature of the method or through a mechanism such as an idempotency key. One describes the concept, and the other describes the system.
Can a POST request be idempotent?
Yes, it can, but not by default. RFC 9110 does not treat POST as idempotent, because every call may create a new record. Still, if the client sends a unique idempotency key and the server stores it, the POST flow behaves idempotently in practice. Most payment providers recommend this approach in their own documentation.
Why is a GET request considered idempotent?
GET exists only to read information and should not change server state. RFC 9110 lists GET among the safe methods, and every safe method is also idempotent. However, if your API deletes a record or raises a counter on a GET request, you break the standard, and systems such as caches will cause trouble.
How long should I keep an idempotency key?
The duration depends on how long a repeat can realistically arrive and on your storage capacity. The key should live at least as long as a client could reasonably keep retrying. Payment providers state their own retention in their documentation, so check the current value there. Also consider privacy rules for responses that contain personal data.
Does idempotency completely prevent double charges?
No, it is not enough alone, but it is one of the strongest defenses. A double charge can still happen if the key is generated wrongly, changes on every attempt, or is stored apart from the business logic. Also, if a customer places two separate orders, those are two separate intents. Monitoring, reconciliation, and testing remain necessary.
Is idempotency only needed for payment systems?
No. It helps wherever repetition is a risk, such as orders, stock, email sending, webhook handling, queue consumers, and cloud functions. Money is the most visible example. Yet a repeated notification or a support ticket opened twice also hurts the customer experience, so it is a question worth asking in every integration.
  • idempotency
  • idempotent api
  • idempotency key
  • http methods
  • retry
  • double charge
  • api design
Share:
Talha Aslan

Google Partner digital marketing expert. Hands-on with SEO, Google Ads, web design and e-commerce projects since 2012; every post here comes from that experience.

Next project

Let's talk about your project.

Your brief goes straight to Talha Aslan and team: strategy led by Talha, delivery by an experienced team. The first consultation is free; we listen and come back with a clear roadmap.