Is POST idempotent?
According to the HTTP specification, the POST method is officially non-idempotent. This means a repeated POST call with the same data may lead to a different result.
Junior Level
No, POST is not idempotent.
According to the HTTP specification, the POST method is officially non-idempotent. This means a repeated POST call with the same data may lead to a different result.
POST = action. Each action is new. Two clicks on “Buy” = two purchases. In serious systems this is solved with a special idempotency key — see below.
Why is POST not idempotent?
POST is typically used for:
- Creating resources:
POST /orderswill create a new order. Two calls = two orders - Processing data:
POST /emails/sendwill send two emails - Adding data: Each call adds something new
POST /orders → Order #1 created
POST /orders → Order #2 created (different result!)
POST /orders → Order #3 created (yet another result!)
What does this mean in practice?
- POST cannot be automatically retried on network errors
- Each POST may create a new side effect
- Browsers warn when resubmitting a POST form
Middle Level
RFC 9110 and POST Non-Idempotency
POST is designed to process data according to the resource’s logic. This is a fundamental characteristic that determines the behavior of browsers, proxy servers, and developers.
PRG Pattern (Post/Redirect/Get)
Classic solution to POST non-idempotency in web interfaces:
- User clicks “Pay” (POST)
- Server processes the payment and sends code 303 See Other with the success page URL
- Browser makes a GET to the success page
Result: On pressing F5, a safe GET is repeated, not a dangerous POST that charges money.
POST for Search
When a GET request is too long (many filters), POST /search is used. Technically it’s idempotent (read-only), but semantically it’s POST.
Implementing Idempotency for POST
In Enterprise systems (Stripe, Adyen, AWS), POST is made idempotent using an Idempotency-Key:
- Client generates a UUID and passes it in a header
- Server uses a Distributed Lock (Redis) by key
user_id + idempotency_key - If the request is already in progress —
409 Conflict - If already completed — return the saved result
Senior Level
Exactly-Once Delivery
In queue-based systems (Kafka, RabbitMQ), POST is often the entry point. Without idempotency, it’s impossible to guarantee “exactly once” message processing on producer retries.
Database Unique Constraints
The cheapest way to ensure POST idempotency — a unique index on business keys (e.g., order_number). However, this shifts logic to the DB level.
Idempotency-Key Pattern (details)
- A Distributed Lock (e.g., Redis) is used by key
user_id + idempotency_key - If a request with this key is already in progress —
409 Conflict - If already completed — return the saved result (201 Created becomes 200 OK on retry)
Edge Cases
When POST non-idempotency is a feature
When a user creates a new comment, each POST should create a new entry. Non-idempotency here is desired behavior, not a problem.
POST for Search
For such cases, the QUERY method has been proposed (draft, not yet a standard). In practice, POST /search is used.
Partial Failure
If a POST should create 3 entities, but only created 2? On retry with the same key, the server must be able to “finish creating” the missing ones or return an error.
Diagnostics and Monitoring
- Double-Submit Detection: Monitoring requests with the same body from the same user within a short window (500ms) helps detect UI bugs
- Header:
X-Retry-Count: Useful to add on the client side, so the server understands whether a POST is a first attempt or an automatic retry - Track repeated POST requests — this is an indicator of network problems or client-side bugs
Interview Cheat Sheet
Must know:
- POST is officially non-idempotent per HTTP specification (RFC 9110)
- Each POST may create a new resource or side effect (5 POST /orders = 5 orders)
- POST cannot be automatically retried on network timeout — risk of double payments
- PRG pattern (Post/Redirect/Get) solves form resubmission: POST -> 303 -> GET
- Idempotency-Key (UUID in header) + Redis lock makes POST idempotent
- Unique Constraints at the DB level — the cheapest way to ensure POST idempotency
- POST non-idempotency is a feature when creating new comments, orders, payments
Frequent follow-up questions:
- How does Stripe make POST idempotent? — Client passes Idempotency-Key, server stores result in Redis
- What is Exactly-Once Delivery? — Guarantee that a message is processed exactly once, despite retries
- Why POST /search when there’s GET? — When GET URL is too long for many filters
- What to do on Partial Failure of POST? — Server must be able to “finish creating” missing items on retry with the same key
Red flags (DO NOT say):
- “POST can be safely retried” — this risks double operations
- “POST is never idempotent” — it can be via Idempotency-Key
- “POST /search is a REST violation” — acceptable when GET URL exceeds limits
- “POST and PUT are the same for updates” — PUT replaces entirely, POST creates/processes
Related topics:
- [[What is idempotency]]
- [[Which HTTP methods are idempotent]]
- [[Why GET and DELETE are idempotent]]
- [[What are the main HTTP methods used in REST]]
- [[Should you use verbs in URL]]