Idempotent Endpoints: Different Responses, Same Server State
Understanding How HTTP Responses Can Vary While Maintaining Idempotency
A common misconception about idempotent endpoints is that they must return identical responses for consecutive calls. However, according to RFC 9110 Section 9.2.2, idempotency is about maintaining the same server state, not necessarily the same response codes or content.
🎯 Key Insight
Idempotency guarantees that multiple identical requests have the same effect on server state, but the HTTP response can legitimately differ between calls.
RFC 9110: The Authoritative Definition
The HTTP Semantics specification (RFC 9110) defines idempotent methods as those where "multiple identical requests have the same effect as a single request." This definition focuses on the effect rather than the response.
"A request method is considered 'idempotent' if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request." — RFC 9110, Section 9.2.2
Notice how the specification emphasizes "intended effect on the server" rather than response consistency.
Examples
PUT: Create vs Update
The most common example is a PUT request that creates a resource on the first call and updates it on subsequent calls:
PUT /api/users/123 HTTP/1.1
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com"
}
// First call - resource doesn't exist
// Response: 201 Created
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2025-07-26T10:00:00Z",
"updated_at": "2025-07-26T10:00:00Z"
}
// Second call - resource exists with same data
// Response: 200 OK
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2025-07-26T10:00:00Z",
"updated_at": "2025-07-26T10:00:00Z" // No change
}
The server state is identical after both calls (user 123 exists with the specified data), but the response codes differ to accurately reflect what happened.
DELETE: Success vs Already Gone
DELETE /api/users/123 HTTP/1.1
// First call - user exists and is deleted
// Response: 200 OK or 204 No Content
{
"message": "User deleted successfully"
}
// Second call - user already deleted
// Response: 404 Not Found or 410 Gone
{
"error": "User not found",
"code": "USER_NOT_FOUND"
}
Both calls achieve the same server state (user 123 doesn't exist), but the responses rightfully differ.
POST with Idempotency Keys
POST /api/payments HTTP/1.1
Content-Type: application/json
Idempotency-Key: payment-abc-123
{
"amount": 100.00,
"recipient": "user-456"
}
// First call - payment processed
// Response: 201 Created
{
"id": "pay_xyz789",
"amount": 100.00,
"status": "completed",
"created_at": "2025-07-26T10:00:00Z"
}
// Duplicate call with same idempotency key
// Response: 409 Conflict (or 200 OK with existing resource)
{
"id": "pay_xyz789",
"amount": 100.00,
"status": "completed",
"created_at": "2025-07-26T10:00:00Z",
"error": "Payment already processed with this idempotency key"
}
The payment state is identical (exactly one payment processed), but the response code indicates a duplicate request was detected.