HTTP Methods Explained: GET, POST, PUT, PATCH, DELETE
A practical walkthrough of HTTP methods — semantics, safety, idempotency, body conventions, and the differences between POST, PUT, and PATCH. Plus OPTIONS and HEAD.
What you'll learn
- ✓What each HTTP method means and when to use it
- ✓The difference between safe and idempotent — and why retries depend on it
- ✓When to use POST vs PUT vs PATCH (the eternal question)
- ✓Which methods take a body and which don't
- ✓The roles of OPTIONS and HEAD
Prerequisites
- •A rough idea of HTTP requests and responses
- •Useful background: What Is REST?
HTTP methods are the verbs of the web. They tell the server what the client wants to do with a resource. Most developers can name five — GET, POST, PUT, PATCH, DELETE — but the rules behind those names are what make APIs predictable, retryable, and cache-friendly.
This post walks through each method, the safety and idempotency rules, and the conventions worth following.
Safe vs idempotent — the two key terms
Two HTTP concepts come up over and over. They are easy to confuse:
- Safe — the method does not change server state. Browsers can prefetch safe requests freely. Examples: GET, HEAD, OPTIONS.
- Idempotent — calling the method N times has the same effect as calling it once. The client can retry safely without fear of duplicates. Examples: GET, PUT, DELETE.
Every safe method is idempotent (doing nothing once is the same as doing nothing twice). The converse is not true — DELETE is idempotent but not safe.
| Method | Safe | Idempotent | Has body | Has response body |
|---|---|---|---|---|
| GET | Yes | Yes | No | Yes |
| HEAD | Yes | Yes | No | No |
| OPTIONS | Yes | Yes | No | Yes |
| POST | No | No | Yes | Yes |
| PUT | No | Yes | Yes | Yes |
| PATCH | No | No (by spec) | Yes | Yes |
| DELETE | No | Yes | Optional | Optional |
Keep this table near you the first few times you build an API. After that it becomes reflex.
GET — read a resource
GET asks for a representation of a resource. It must be safe: a GET should never create, modify, or delete anything.
GET /users/42 HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 42, "name": "Alice" }
A few conventions:
- No request body. Servers are allowed to ignore one if you send it. Some load balancers strip it. Just don’t.
- Cacheable by default. Browsers, CDNs, and proxies all assume GETs are cacheable unless told otherwise.
- Idempotent and safe means a failed GET can always be retried.
If you’re tempted to “GET” something that has side effects (like marking a notification read), that’s a POST, not a GET. The web crawls everything; a GET with side effects is a foot-gun.
POST — create or act
POST is the catch-all “do something” verb. It’s the only common method that is neither safe nor idempotent. Two patterns dominate.
Create a new resource
POST /users HTTP/1.1
Content-Type: application/json
{ "name": "Alice", "email": "alice@example.com" }
HTTP/1.1 201 Created
Location: /users/42
{ "id": 42, "name": "Alice", "email": "alice@example.com" }
The server assigns the ID. The response is 201 Created with a Location header pointing to the new resource — see HTTP Status Codes Explained.
Trigger an action
POST /orders/9001/cancel HTTP/1.1
HTTP/1.1 200 OK
{ "id": 9001, "status": "cancelled" }
When the action isn’t a clean state transition (cancel, archive, retry a job), POST is the right verb. Don’t twist yourself into a noun-shaped pretzel to avoid it.
POSTs aren’t idempotent by default. If the network drops and the client retries, you might create two users. The fix is the Idempotency-Key header — covered in REST API Design.
PUT — replace a resource
PUT replaces the resource at the given URL with whatever the client sends. It is idempotent: send the same body twice, and the end state is the same.
PUT /users/42 HTTP/1.1
Content-Type: application/json
{ "name": "Alice Cooper", "email": "alice@example.com", "role": "admin" }
HTTP/1.1 200 OK
{ "id": 42, "name": "Alice Cooper", "email": "alice@example.com", "role": "admin" }
The key idea: PUT means “make the resource look like this.” If you leave out a field, you are saying “set that field to its default” — typically null or absent.
That last point catches people. PUT is not partial update. If the existing user had a bio field and your PUT doesn’t include it, the bio should be cleared.
If you want partial update, you want PATCH.
PATCH — modify part of a resource
PATCH applies a change to a resource. Only the fields you send are updated; everything else is untouched.
PATCH /users/42 HTTP/1.1
Content-Type: application/json
{ "role": "admin" }
HTTP/1.1 200 OK
{ "id": 42, "name": "Alice Cooper", "email": "alice@example.com", "role": "admin" }
By specification, PATCH is not guaranteed idempotent — a patch could be an instruction like “increment by 1” which isn’t idempotent. In practice, most PATCH endpoints do the simple thing (set named fields) and are de-facto idempotent.
Two PATCH conventions you’ll encounter:
- Merge patch (RFC 7396) — send a partial JSON object; fields you include are set, fields you omit are untouched, fields set to
nullare cleared. Simple and common. - JSON Patch (RFC 6902) — send an array of explicit operations like
[{ "op": "replace", "path": "/role", "value": "admin" }]. Powerful but rarely used in everyday APIs.
Stick with merge patch unless you have a specific reason.
POST vs PUT vs PATCH
The eternal question. The clearest framing:
| Operation | Method | Idempotent? | Client supplies the ID? |
|---|---|---|---|
| Create a new resource | POST | No | No — server assigns |
| Create or replace at a known URL | PUT | Yes | Yes — client supplies |
| Replace an existing resource | PUT | Yes | n/a |
| Update some fields | PATCH | Often, but not required | n/a |
| Trigger an action | POST | No | n/a |
When is PUT actually used to “create”?
When the client owns the namespace. For example, an API that lets you upload a file by name:
PUT /files/avatars/alice.png
Content-Type: image/png
<bytes>
If the file doesn’t exist, PUT creates it. If it does, PUT replaces it. Both calls produce the same end state — that’s idempotency.
For resources with server-assigned IDs (almost all REST APIs in practice), creation is POST.
Try it yourself. Pick the right method for each operation on a /posts API:
- Get a list of posts
- Create a new post (server assigns the ID)
- Replace post 9 entirely
- Change just the title of post 9
- Delete post 9
- Publish post 9 (a workflow action with side effects)
Answers: GET, POST, PUT, PATCH, DELETE, POST /posts/9/publish.
DELETE — remove a resource
DELETE removes the resource at the given URL. Like PUT, it’s idempotent: calling DELETE on the same URL twice should produce the same end state (the resource is gone).
DELETE /users/42 HTTP/1.1
HTTP/1.1 204 No Content
A subtle question: what should the second DELETE return? Two valid answers:
- 204 No Content every time — emphasises idempotency. The user is gone; the server returns success.
- 404 Not Found on the second call — reflects that there’s no such resource any more.
Either is defensible. Many APIs return 204 on success and 404 if the resource never existed, which is a reasonable middle ground.
A DELETE can have a body (the spec recently clarified this), but treating it as bodyless is more compatible. Pass options as query parameters if you need them:
DELETE /users/42?reason=spam HTTP/1.1
HEAD — like GET, without the body
HEAD is GET with the body stripped. Same headers, same status code, no payload.
HEAD /large-file.zip HTTP/1.1
HTTP/1.1 200 OK
Content-Length: 1048576
Last-Modified: Mon, 16 Jun 2026 10:00:00 GMT
ETag: "abc123"
Use HEAD to:
- Check if a resource exists without downloading it
- Get the size before deciding whether to fetch
- Validate a cached copy via
ETag/Last-Modified
Most REST APIs don’t implement HEAD explicitly because web frameworks handle it automatically when GET exists.
OPTIONS — what can I do here?
OPTIONS asks the server what methods (and other capabilities) are allowed for a resource:
OPTIONS /users/42 HTTP/1.1
HTTP/1.1 204 No Content
Allow: GET, PUT, PATCH, DELETE
In the modern web, OPTIONS is best known as the CORS preflight request. Browsers fire it before a “non-simple” cross-origin request to confirm the server permits it:
OPTIONS /users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
If your frontend can’t reach your API, the first thing to check is the OPTIONS response and CORS headers.
Bodies and content types
Quick reminders that catch people:
- GET and HEAD do not have bodies. Pass data via query string or headers.
- POST, PUT, and PATCH have bodies. Set
Content-Typecorrectly — usuallyapplication/json. - DELETE may have a body, but most servers and proxies don’t expect one. Prefer query parameters.
- The response body depends on the status code. 204 means no body; 200/201 typically include one.
A common mistake on the server: trying to read req.body on a GET request. There’s nothing there.
Retries and the network
This is where safety and idempotency earn their keep.
Networks fail mid-flight. If a request times out, the client doesn’t know if the server processed it or not. What’s safe to retry?
- GET, HEAD, OPTIONS — always safe to retry.
- PUT, DELETE — idempotent; safe to retry, the end state is the same.
- PATCH — usually safe; depends on the implementation.
- POST — not safe to retry. Two POSTs may create two resources.
For POSTs, the fix is to make them idempotent at the application level using an Idempotency-Key header. The client generates a UUID per operation; the server deduplicates by that key. See REST API Design.
This is why every serious payments API in the world supports idempotency keys. The cost of a duplicate charge is enormous; the fix is a few lines of header handling.
Try it yourself. For each scenario, decide whether the client can safely retry on a network failure:
GET /users/42timed outPOST /payments(no idempotency key) timed outPUT /users/42timed outDELETE /users/42returned 502 from a gatewayPATCH /users/42with{ "role": "admin" }timed outPOST /paymentswithIdempotency-Key: abc-123timed out
Answers: 1 yes, 2 no, 3 yes, 4 yes, 5 yes (it’s “set role to admin”; safe to repeat), 6 yes (the server will return the original response).
Status codes per method
A rough mapping of which status codes naturally pair with which methods:
| Method | Common success codes | Common error codes |
|---|---|---|
| GET | 200, 304 | 401, 403, 404 |
| POST (create) | 201 | 400, 401, 403, 409, 422 |
| POST (action) | 200, 202 | 400, 401, 403, 409 |
| PUT | 200, 204 | 400, 401, 403, 404, 409 |
| PATCH | 200 | 400, 401, 403, 404, 409, 422 |
| DELETE | 204 | 401, 403, 404 |
For the full status code tour, see HTTP Status Codes Explained.
A pragmatic checklist
When you wire a new endpoint, ask:
- Does the method match the intent? (GET for read, POST for action, etc.)
- Is the operation idempotent under retry?
- For POST that creates, does the response include a Location and 201?
- For DELETE, do you return 204 on success?
- For PATCH, are you using merge semantics (only sent fields change)?
- Have you handled OPTIONS for CORS?
That’s most of the practical surface of HTTP methods.
Recap
You now know:
- Safe means no state change; idempotent means safe to repeat
- GET reads; POST creates or acts; PUT replaces; PATCH modifies; DELETE removes
- HEAD is bodyless GET; OPTIONS discovers capabilities and powers CORS
- PUT sets the whole resource; PATCH changes named fields
- POST is the only common method that’s neither safe nor idempotent — use idempotency keys to make it retryable
Next steps
Pair this with HTTP Status Codes Every API Developer Should Know, then sweep through REST API Design: Practical Best Practices to put it all together. For a deeper look at the architectural style underneath, revisit What Is REST?.
Questions or feedback? Email codeloomdevv@gmail.com.