Idempotency and Safety
Understanding idempotency and safety is crucial for designing reliable REST APIs. These properties affect how clients can retry requests and how servers should handle them.
What is Safety?
A method is safe if it doesn't modify the server state. Safe methods are read-only operations.
| Method | Safe? |
|---|---|
| GET | Yes |
| HEAD | Yes |
| OPTIONS | Yes |
| POST | No |
| PUT | No |
| PATCH | No |
| DELETE | No |
Why it matters:
- Safe methods can be cached
- Safe methods can be prefetched
- Safe methods can be retried without concern
# Safe - can be called any number of times
GET /api/users/123
# Not safe - each call may change state
POST /api/users
What is Idempotency?
A method is idempotent if calling it multiple times has the same effect as calling it once. The server ends up in the same state regardless of how many times the request is made.
| Method | Idempotent? |
|---|---|
| GET | Yes |
| HEAD | Yes |
| OPTIONS | Yes |
| PUT | Yes |
| DELETE | Yes |
| POST | No |
| PATCH | Sometimes |
Examples
Idempotent (PUT):
PUT /api/users/123
{ "name": "Alice", "email": "alice@example.com" }
# Calling this 1 time or 100 times results in the same user data
Not Idempotent (POST):
POST /api/orders
{ "product": "Widget", "quantity": 1 }
# Calling this twice creates TWO orders!
Idempotent (DELETE):
DELETE /api/users/123
# First call: deletes user, returns 200/204
# Second call: user already gone, returns 404
# But server state is the same: user is deleted
Why Idempotency Matters
Network Reliability
Networks are unreliable. A client might:
- Send a request
- Server processes it
- Response gets lost
- Client doesn't know if it worked
- Client retries
If the method is idempotent, retrying is safe. If not, you might create duplicate resources.
Client Server
| |
|----POST /orders-------->|
| | (creates order #1)
| <---(lost)-----------|
| |
|----POST /orders-------->| (retry)
| | (creates order #2!)
|<-------201 Created------|
Making POST Idempotent
You can make POST idempotent using idempotency keys:
POST /api/orders
Idempotency-Key: abc-123-unique
Content-Type: application/json
{ "product": "Widget", "quantity": 1 }
The server:
- Checks if it's seen this idempotency key
- If yes, returns the same response as before
- If no, processes the request and stores the key
Exercise: Identify Idempotent Operations
PATCH Idempotency
PATCH is a special case. It can be idempotent or not, depending on the operation:
Idempotent PATCH (set operation):
PATCH /users/123
{ "email": "new@example.com" }
# Calling multiple times = same email
Non-Idempotent PATCH (increment operation):
PATCH /users/123
{ "loginCount": { "$increment": 1 } }
# Calling twice = count increases by 2
Best Practices
1. Document Idempotency
Tell API consumers which operations are idempotent:
paths:
/orders:
post:
description: Creates a new order
x-idempotent: false # or use Idempotency-Key header
2. Support Idempotency Keys for POST
For critical operations, accept an idempotency key:
POST /api/payments
Idempotency-Key: pay_abc123_attempt_1
3. Return Consistent Responses
For idempotent operations, return the same response on retries:
# First DELETE
DELETE /users/123
-> 204 No Content
# Second DELETE (user already gone)
DELETE /users/123
-> 404 Not Found # or 204 - both are valid approaches
Summary Table
| Property | Means | Methods |
|---|---|---|
| Safe | Doesn't modify state | GET, HEAD, OPTIONS |
| Idempotent | Same result on retries | GET, PUT, DELETE, HEAD, OPTIONS |
| Neither | Can create duplicates | POST |
Understanding these properties helps you:
- Design reliable APIs
- Handle network failures gracefully
- Implement proper retry logic
- Avoid duplicate resource creation

