toolhq.io

All posts
July 8, 20268 min read

GET vs POST vs PUT vs PATCH: HTTP methods explained

Every HTTP request starts with a method, and picking the right one is less about rules and more about contracts. The method tells the server, caches, proxies, and every other developer what your request is allowed to do. Get the contract right and the web's infrastructure works with you. Get it wrong and you end up with cached writes, retried payments, and forms that resubmit on refresh.

The four you use every day

  • GET reads a resource. It has no body by convention, changes nothing on the server, and can be cached, bookmarked, and prefetched freely.
  • POST submits data to be processed. It usually creates something new or triggers an action, and the server decides what happens. Two identical POSTs can create two records.
  • PUT replaces a resource at a known URL with the body you send. Send the whole object; whatever was there before is gone.
  • PATCH applies a partial update. Send only the fields you want to change and the rest stay as they were.

There are others (DELETE removes a resource, HEAD is GET without the body, OPTIONS asks what is allowed and powers CORS preflights), but the four above cover most API work.

Safe and idempotent: the two properties that matter

Two words from the HTTP spec explain almost every method decision.

A method is safe if it does not change server state. GET, HEAD, and OPTIONS are safe. That is why browsers prefetch GET links and why a crawler can follow every GET on your site without breaking anything. If your GET endpoint deletes rows, a crawler will eventually delete your rows.

A method is idempotent if sending the same request twice has the same effect as sending it once. GET, PUT, DELETE, HEAD, and OPTIONS are idempotent. POST is not, and PATCH is not guaranteed to be.

MethodSafeIdempotentTypical use
GETyesyesread a resource
POSTnonocreate, or trigger an action
PUTnoyesreplace at a known URL
PATCHnonot guaranteedpartial update
DELETEnoyesremove a resource

Idempotency is not academic. Networks fail mid-request, and clients, proxies, and retry libraries will resend requests they believe are idempotent. A retried PUT overwrites with the same data, harmless. A retried POST charges the card twice. This is why payment APIs like Stripe ask for an idempotency key on POST requests: it manually adds the property POST lacks.

GET vs POST in practice

The classic confusion is forms and query strings. Use GET when the request is a question: searches, filters, pagination. The parameters go in the URL, the result can be cached and shared, and the back button behaves. Use POST when the request is an instruction: sign up, place order, send message.

Two practical differences follow from that:

  • URL length and visibility. GET parameters live in the URL, so they end up in browser history, server logs, and analytics. Never put passwords or tokens in a query string, even over HTTPS. POST bodies are not logged by default.
  • Caching. GET responses are cached by browsers and CDNs unless headers say otherwise. POST responses are not. If your "read" endpoint is a POST (common with complex search payloads), you give up HTTP caching entirely.

PUT vs PATCH in practice

Both update, and the difference is what the body means.

PUT says: here is the complete new state of this resource. PUT /users/42 with {"name": "Ada"} means the user is now exactly that object. If the old record had an email field, a strict PUT removes it. This full-replacement semantic is what makes PUT idempotent: applying the same complete state twice lands you in the same place.

PATCH says: here are the changes. PATCH /users/42 with {"name": "Ada"} updates the name and leaves everything else alone. That is almost always what an edit form wants, which is why most real APIs lean on PATCH for updates even when their docs say PUT.

PATCH is only non-idempotent when the patch is relative, for example {"op": "increment", "field": "count"}. A plain "set these fields" PATCH is effectively idempotent, but the spec does not promise it, so infrastructure will not auto-retry it.

Common mistakes worth avoiding

  • Tunneling everything through POST. Some clients do this to dodge CORS or old proxy issues. You lose caching, retries, and the self-documentation the method provides.
  • GET with a body. The spec technically allows it, but many servers, proxies, and CDNs drop or reject GET bodies. Elasticsearch's search API is the famous exception, and even it accepts POST for exactly this reason.
  • Using 200 for everything. Method choice pairs with status codes: POST that creates should return 201 Created, DELETE often returns 204 No Content. The HTTP status codes guide covers the full set.
  • Redirecting a POST with 301. Most clients turn the retried request into a GET. If the method must survive a redirect, use 307 or 308.

Try the differences yourself

The fastest way to internalize method behavior is to send real requests and watch what comes back. The API Tester lets you switch between GET, POST, PUT, PATCH, and DELETE against any endpoint, edit headers and bodies, and inspect the response status and headers, straight from your browser with nothing proxied through a server.