Fundamentals
Safe methods and idempotency
Each HTTP method makes a promise about side effects. Knowing which methods are safe to repeat tells you when a retry is harmless and when it could charge a card twice.
A client sends a request and the connection drops. Did the server act on it? If the client sends it again, will anything break? HTTP answers these questions with two promises per method. A method is safe if it has no side effects. A method is idempotent if doing it twice has the same effect as doing it once.
Concept
Before you start, a method is the verb of an HTTP request, such as GET or POST. A retry is sending the same request again after a failure. Both are covered in HTTP.
The big idea
Good REST design treats URLs as resources, which are things like files, users or payments. You act on them with a small set of verbs. Some conventions keep this predictable.
- Plural nouns and stable IDs. Use
/files/fl_9, not/getFile?id=9. Prefixed IDs such asfl_andu_make logs readable and catch a user ID passed where a file ID was expected. - Shallow nesting. List files under a folder, but fetch one file directly at
/files/fl_9. - One naming style. Use RFC 3339 timestamps in UTC, and store money as integer minor units such as cents, never floats.
The method table below is what callers and proxies rely on when they decide whether to retry.
Walking through it
| Method | Safe | Idempotent | Typical use |
|---|---|---|---|
| GET | Yes | Yes | Read |
| PUT | No | Yes | Replace a resource at a known URL |
| DELETE | No | Yes | Delete. A second call returns 404 or 204, and the state is the same |
| PATCH | No | Not guaranteed | Partial update |
| POST | No | No | Create, or trigger an action |
POST is the risky one. Follow a payment through a timeout:
- The client sends
POST /v1/paymentsfor 4,200 cents. - The server charges the card, but the response is lost in the network.
- The client times out and can't tell whether the charge happened.
- A plain retry charges the card a second time.
The fix is an idempotency key. The client creates one key per logical operation and sends it in a header. The server stores the result under that key and returns the stored result for any retry.
POST /v1/payments HTTP/1.1
Idempotency-Key: 6f1c2a9e-8d4b-4f8e-9a51-2c7e0b3d1f44
Content-Type: application/json
{"amount": 4200, "currency": "usd", "source": "card_abc"}Now the retry finds the stored result for that key, so the card is charged once. The idempotency page covers how the server stores those results.
Common mistakes
- Retrying a POST without a key. The client can't know whether the first attempt worked, so the retry may repeat the action.
- Generating a new key on each retry. The key names one logical operation. A new key per attempt gives you no protection.
- Assuming PATCH is idempotent. The HTTP spec doesn't guarantee it. Check what your PATCH does when it runs twice.
Check yourself
- Is DELETE idempotent? Yes. The second call finds nothing to delete, so the end state is the same even though the status code may change.
- Why is a timed-out POST dangerous to retry? The client doesn't know if the first attempt succeeded, so a retry can repeat the side effect unless an idempotency key is sent.
Key takeaways
- Safe means no side effects. Idempotent means the same end state after one call or many.
- GET, PUT and DELETE are idempotent. POST is not, so it needs an idempotency key to retry safely.
- Give every create or action endpoint a key, and store its result for retries.
Further learning
The idempotency page explains how to store results for keys.
Next, read about cursor pagination, which applies the same care to lists.
Related
API design
An API is a contract that outlives the code behind it. Design the styles, methods, pagination, errors and async flows so changes stay additive.
Idempotency and retries
Making operations safe to retry, with idempotency keys, deduplicating consumers and jittered backoff, so at-least-once delivery never means a double charge.
HTTP/1.1, HTTP/2 and HTTP/3
The protocol of the web. Separates HTTP's meaning (methods, status codes, caching) from its wire format, and shows how each version fixes the latency problem of the one before.