DEV Community

Cover image for Idempotency: The Small API Concept That Prevents Big Problems
Aasim Ghaffar
Aasim Ghaffar

Posted on

Idempotency: The Small API Concept That Prevents Big Problems

When building APIs, developers usually spend a lot of time thinking about authentication, validation, performance, and database design.
But there is another concept that can quietly prevent some very serious production problems: idempotency.
It becomes especially important when an API performs an action that should happen only once—such as creating a payment, placing an order, registering a user, or submitting a booking.
Without idempotency, a simple network problem can sometimes turn one user action into multiple operations.

What Is Idempotency?

In simple terms, an API operation is idempotent when making the same request multiple times produces the same intended result as making it once.
Imagine a user clicks “Pay Now.”
The request reaches the server, the payment is processed, but the response is delayed because of a network issue.
The user's browser does not know whether the payment succeeded.
The user clicks the button again.
Now the server receives the same operation twice.
Without protection, the application might process two payments.
With an idempotent design, the server can recognise that the second request represents the same operation and return the result of the original request instead of processing it again.
That is the basic idea.

Why Does This Matter?

Networks are not perfectly reliable.
Requests can be retried because of:
Temporary connection failures
Browser retries
Mobile network problems
API gateways
Load balancers
Background workers
Client-side retry logic
Third-party services
Timeouts
The important point is that a timeout does not necessarily mean the operation failed.
The server may have completed the operation successfully, while the client simply never received the response.
This creates an important question:
What should happen when the client sends the same request again?
That is where idempotency becomes valuable.

A Simple Example

Consider an order API:
POST /api/orders

The client sends:
{
"product_id": 123,
"quantity": 1
}

If the request times out and the client retries it, the server may create:
Order #1001
Order #1002

even though the customer only intended to place one order.
A common solution is to provide an idempotency key:
Idempotency-Key: 8f4a7c21-...

The client generates a unique key for the operation and sends it with the request.
The server stores that key together with the result.
If the same key arrives again, the application knows that the operation has already been processed.
Instead of creating another order, it can return the original result.

Idempotency Keys Are About the Operation

One important detail is that an idempotency key should represent a specific operation, not simply a user.
For example:
User: 42
Idempotency-Key: payment-abc123

If the user makes another legitimate payment, it should receive a different key.
Otherwise, the application could incorrectly treat a new operation as a duplicate.
A better mental model is:
One business operation → One idempotency key

Where Should the Key Be Stored?

The implementation depends on the application, but a database is often a practical choice.
A simplified table might contain:
idempotency_key
request_hash
status
response_code
response_body
created_at

When a request arrives, the application can:
Check whether the key already exists.
If it does not exist, create a record.
Process the operation.
Store the result.
Return the response.
If the same key arrives again, return the stored result.
The exact implementation needs to account for concurrency, transactions, expiration, and failure scenarios.

The Concurrency Problem

There is a subtle problem with a simple implementation.
Imagine two identical requests arrive at almost exactly the same time:
Request A → Check key → Not found
Request B → Check key → Not found

Both requests might then continue processing.
That defeats the purpose of idempotency.
This is why the idempotency key usually needs a database uniqueness constraint or another atomic mechanism.
For example:
UNIQUE(idempotency_key)

The database can then help guarantee that two requests cannot successfully create separate records for the same key.
This is a good example of why reliable API design is not just about writing application code. Database constraints can be part of the application's correctness model.
What About Failed Requests?
Another important design decision is deciding what to store.
Suppose a request creates an order but something fails afterwards.

Should the idempotency key be reusable?

There is no universal answer.
The behaviour should be defined according to the business operation.
For example, a payment API may need to preserve the result of a completed payment attempt, while another type of operation might allow the client to retry after a validation or temporary server failure.
The important thing is to define the lifecycle clearly rather than treating every failure as a simple retry.
Idempotency Is Not the Same as Duplicate Validation
It is tempting to think:
“I'll just check whether this order already exists.”
But duplicate detection and idempotency are not necessarily the same thing.
An application might legitimately receive two orders with similar data.
For example:
Same customer
Same product
Same quantity

does not automatically mean the second order is a duplicate.
An idempotency key gives the system a way to identify the same intended operation.
That distinction becomes particularly important in payment, booking, checkout, and integration systems.
Where I Find Idempotency Most Useful
Idempotency becomes especially valuable when an operation has a real-world side effect.
Examples include:

  • Payments
  • Orders
  • Bookings
  • Account creation
  • Subscription changes
  • Sending external requests
  • Webhook processing
  • Background jobs
  • Financial transactions It is also useful when integrating with third-party APIs where retries are unavoidable. For example, if your application sends a booking request to an external service and does not receive a response, blindly sending the request again could potentially create a duplicate booking. The integration needs a strategy for safely retrying the operation. A Practical Rule A useful rule I follow is: If repeating an API request could create an unwanted side effect, think about idempotency before putting the endpoint into production. Not every endpoint needs an idempotency key. A read operation such as: GET /api/products/123

does not normally need one because repeating the request does not create another product.
But an operation such as:
POST /api/payments

may require much more careful handling because repeating it could create another financial transaction.

Final Thoughts

Idempotency is a relatively small concept, but it solves a problem that becomes much bigger at production scale.
Applications operate across unreliable networks, distributed services, queues, browsers, mobile devices, payment providers, and third-party APIs.
Retries are inevitable.
The goal is not to prevent retries. The goal is to make retries safe.
Good API design therefore isn't only about making the first request work. It is also about deciding what happens when the same request arrives again.
That is why idempotency deserves a place in the design of any API where duplicate operations could have real consequences.

Top comments (0)