Answers · The network and the web
What does it mean for an API method to be safe?
In shortA safe method is one that changes nothing on the server when it is called, so a caller may repeat it freely and a cache or a crawler may use it without anything being altered.
Safe means calling it changes nothing that a caller can observe on the server. Read the state before, read the state after, and the two are identical: the method reports rather than acts, so a caller may send it as often as it likes, a shared cache may answer it without troubling the origin, and a crawler may follow it into every corner of the service. Safety is a claim about effects, not about the shape of the answer and not about the verb the method happens to be named after.
Take a method that returns one order. Before the call, order 4471 has one row in the orders table, its status reads paid, and the customer's balance is 120.00. Call the method three times in a row and look at those same three facts after each call.
| when | rows for order 4471 | status | balance |
|---|---|---|---|
| before | 1 | paid | 120.00 |
| after the first call | 1 | paid | 120.00 |
| after the second call | 1 | paid | 120.00 |
| after the third call | 1 | paid | 120.00 |
Nothing moved, so the method is safe. Note that the test is taken on state and not on the response. A method that returns the current time, or a random number, or a fresh page of results is still safe, because what it hands back is not the same question as what it altered. Equally, a method is not safe merely because it is called get: if its handler marks a message delivered, or appends an audit row that a caller later reads back through the API, then it changes something observable and the label is a lie.
Why safe is the stronger claim
Put equal to one in the first line and the second line follows immediately, so every safe operation is repeatable at no extra cost. The converse does not hold, and that gap is exactly where the usual confusion lives: repeatability asks only whether a second call differs from a first, while safety asks whether the first call did anything at all.
Deleting is the clean counterexample. Send the delete for order 4471 twice. The first call takes the table from one row to zero, the second leaves it at zero, the third leaves it at zero again. The state after two calls equals the state after one, so the operation is repeatable. It is plainly not safe, because one row became none. Replacing an order wholesale behaves the same way: the second call writes identical bytes over identical bytes, so repeating costs nothing extra, and yet the first call rewrote everything. That a second call returns 404 instead of 200 changes nothing about the state, and the property is about the state.
So what does the extra word buy, given that retry is already free for anything repeatable? Safety buys the things that need a guarantee about the first call. A shared cache may serve one response to a thousand callers, a prefetcher may issue the call on speculation, a proxy may collapse a hundred in-flight requests into one, and a crawler may walk the whole surface. None of those is legitimate for a method that deletes an order, even though deleting twice is harmless.
Where neither property holds, the caller's retry becomes a gamble, and at volume the gamble always loses. Suppose one request in a thousand reaches the server, is carried out, and is never acknowledged, and that the caller retries every such timeout. The chance a given request produces no duplicate is . Over ten thousand requests a day the chance that none of them duplicates is . Since , that is , so the chance of at least one duplicate in the day is , about 99.995 per cent. Scale down to a hundred requests a day and the same arithmetic gives , which is still a 9.5 per cent chance of a duplicate every day.
Where the claim stops
Safety is a promise about the state the API exposes, not about every byte the machine touches. A read still writes a log line, bumps a rate-limit counter, and may fill a cache; none of those is the resource the caller asked about, and a caller who leans on them is leaning on something safety does not cover. Nor does it say anything about cost or freshness: a read can be the most expensive call in the service, and the licence to repeat it is a licence the caller exercises against your capacity, so quotas and staleness are separate problems that safety leaves untouched.
The commitment runs one way. Adding a side effect to a method that was safe, a counter that appears in a listing, a message that goes out, breaks caches, prefetchers, and every retry layer that trusted the label, and it breaks them without any change to the signature or the name. That is why the classification belongs to the design rather than to the documentation: it is decided by what the code does, and once given it is a compatibility promise.
Worth keeping
- Safety is strictly stronger than repeatability: a safe call is always safe to repeat, but a call can be perfectly repeatable and still change the world, as deleting the same order twice leaves it just as deleted.
- Whether a method is safe is decided by what its code does to the resource, not by the verb in its name or by what it returns.
- Safety buys caching, prefetching and crawling; the freedom to retry is bought by repeatability, which safety happens to include.
Where this goes next
This question is one step of Designing an Interface to Call, a course that works through its subject from the beginning. The page that teaches this part of it is Choosing the Operations, and it reads the whole way through whether or not you have an account.
What an account changes is the practice: problems standing under every idea, marked with a reason rather than a tick, in a copy of the course that is yours.
Read that pageThe whole courseOther questions people ask about this
- What's wrong with naming an API endpoint after an action like /getUser?Naming an endpoint after an action bakes one operation into its address, so every new operation needs a new endpoint and the thing being acted on never gets a stable name.
- How do I design an error response that a client can actually handle?Give every failure a fixed code a program can branch on and say plainly whether trying again could help, then put the human explanation last.
- What's the difference between safe and idempotent in HTTP methods?Safe means the call changes nothing; idempotent means repeating the call leaves the same result, even if the first call changed something.
- Why do I need an idempotency key if my operation is already idempotent?You need the key because repeating an operation safely still leaves the caller unsure what happened, and the key returns the first answer.
- Where should a session ID be stored so JavaScript can't read it?Store it in an HttpOnly cookie set by the server; that is the only browser storage JavaScript running on the page cannot read.
- Does signing out actually invalidate my token on the server?No. Signing out clears your browser's copy of the token, but the server keeps accepting that token until it expires.