What is an Idempotent API?

Michael Faber
Geek Culture
Published in
3 min readAug 8, 2024

A common pattern in modern software engineering.

An operation is considered idempotent if repeating the same request with the same input always produces the same result and does not cause any additional side effects. Consider the following:

function getPi(): number {
return 3.14159;
}

function getDate(): number {
return Date.now();
}

It’s easy to see that the getPi function is idempotent. There is no state to change and the output is always the pi constant.

The getDate function on the other hand is not idempotent. In TypeScript, Date.Now() returns the number of milliseconds since midnight at the beginning of January 1, 1970, UTC. The same input (nothing) will produce different outputs.

Idempotency in APIs

It is considered a best practice to make endpoints of APIs idempotent where they can be. Consider a hypothetical CRUD (Create, Read, Update, Delete) API that manages a database table Persons.

Create

Following REST (REpresentational State Transfer) practices, our CRUD API’s create endpoint will use the HTTP POST verb. It will accept information about a Person as its input and return the ID of the newly created Person as the output:

Request
POST /api/persons

{
"Name": "John Smith"
}

Response
200 OK

{
"ID": 1
}

Create endpoints are not idempotent. It’s clear the same input produces a different ID in the the HTTP response, but that’s not actually what’s important here. Think of the state of the resource the API is managing as the output. A create request will always result in a different state.

Read

Our CRUD API’s read endpoint will be boilerplate. It will use the HTTP GET verb, accept an ID as a URL parameter, and return a Person object:

Request
GET /api/persons/1

Response
200 OK

{
"ID": 1,
"Name": "John Smith",
}

This read endpoint is idempotent. The request above could be called 1000 times and the HTTP response would be the same. Keep it mind though, the HTTP response is not the output when considering idempotency of APIs. We have to consider the state. In a standard database, reading rows is never going to alter the state.

Here is where things begin to get interesting. If you’ve built an API like this before, you are probably thinking to yourself: what if a Person with an ID of 1 doesn’t exist in the database? Suppose the endpoint returned a 404 HTTP code in that case:

Request
GET /api/persons/2

Response
404 NOT FOUND

Then, let’s say another consumer called the create endpoint and created a Person with an ID of 2. The request above would return a different HTTP response, and the state of the database table would be different!

Changing the state of the resource is not what prevents the create endpoint from being idempotent. Idempotency doesn’t mean requests can’t change the state of the resource, that is expected. It means that performing the same operation multiple times with the same input will always produce the same result and have no additional side effects.

Update

The update endpoint serves as a good example of how idempotent endpoints make “the same change” with each request.

Our CRUD API will use the HTTP UPDATE verb, accept an ID as a URL parameter, accept Person information to update, and return the newly updated Person object.

Let’s look at the current state of our Persons database table:

ID  Name
1 John Smith

and after an update request:

Request
UPDATE /api/persons

{
"Name": "Jane Doe",
}

Response
200 OK

{
"ID": 1,
"Name": "Jane Doe",
}
ID  Name
1 Jane Doe

Although we’ve changed the database state, sending the same request again would result in the same state. The update request may change the state of the resource, but it’s idempotent because the same request will always produce the same resource state.

Delete

Removing a Person from the Persons table is fundamentally similar to updating one. Our CRUD API will use the HTTP DELETE verb and accept an ID as a URL parameter:

Request
DELETE /api/persons/1

Response
200 OK

The resource state may be changed, but sending the same request would change it in the same way.

Conclusion

Idempotent operations always produce the same input given the same output. Consider the state of the resource the API is managing as the “output”, and implement endpoints this way to simplify consumer logic.

Thank you for reading!

--

--