Question 8 · Section 6

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.

Language versions: English Russian Ukrainian

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:

  1. Creating resources: POST /orders will create a new order. Two calls = two orders
  2. Processing data: POST /emails/send will send two emails
  3. 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:

  1. User clicks “Pay” (POST)
  2. Server processes the payment and sends code 303 See Other with the success page URL
  3. 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]]