All articles
HTTPAPI DesignSystem Design13 min read

All HTTP Methods Explained: GET, POST, PUT, PATCH, DELETE and the Rest

Every HTTP method with its real semantics — safe vs idempotent, cacheable or not, body allowed or not — plus HEAD, OPTIONS, TRACE, CONNECT and how to pick the right one in an API design interview.

Methods are a contract

A method is a promise about what the request does. That promise is not decoration — the entire infrastructure between your client and your handler reads it and acts on it. A CDN will cache a GET. A browser will retry an idempotent request after a dropped connection. A crawler will happily fire GETs at every link it finds. A retry layer will re-send a PUT and refuse to re-send a POST.

The classic production incident
A team exposes GET /admin/users/42/delete. A search-engine crawler indexes the admin panel, follows every link, and deletes the user table. The method was wrong, so every piece of well-behaved infrastructure did exactly the wrong thing correctly.

Safe, idempotent, cacheable

  • Safe — does not modify server state. Read-only from the caller's point of view. Logging and analytics do not count as modification.
  • Idempotent — sending it N times has the same effect as sending it once. Note this is about effect, not response: DELETE may return 204 then 404 and still be idempotent.
  • Cacheable — a response may be stored and reused. GET and HEAD by default; POST only with explicit freshness headers, which almost nobody does.
Idempotent →Not idempotentSafe + idempotentGET · HEAD · OPTIONScacheable, retryable, crawlableUnsafe + idempotentPUT · DELETEsafe to retry, never cacheUnsafe + not idempotentPOST · PATCH*needs an idempotency key(empty — a safe methodis always idempotent)* PATCH is idempotent only if the patch document is absolute rather than relative.
Safety and idempotency map cleanly onto what infrastructure is allowed to do.

The full method table

MethodSafeIdempotentBodyTypical use
GETYesYesNoFetch a representation
HEADYesYesNoHeaders only — existence, size, ETag
OPTIONSYesYesNoCapabilities; CORS preflight
POSTNoNoYesCreate, or any non-uniform action
PUTNoYesYesFull replace at a known URI
PATCHNoNo*YesPartial update
DELETENoYesDiscouragedRemove the resource
TRACEYesYesNoLoopback diagnostics — disable it
CONNECTNoNoNoTunnel through a proxy (HTTPS)

GET

Retrieve a representation. No body — some servers and proxies silently drop it, so a “GET with a JSON body” search API works in curl and breaks behind a CDN. Put filters in the query string; if the filter is genuinely too large for a URL, use POST /searches that returns a search id, then GET the id.

GET /v1/orders?status=shipped&page=2&limit=50&sort=-createdAt
Pagination and filtering belong in the query string, not the path.
  • Keep URLs under ~2000 characters to stay safe across proxies.
  • Never mutate on GET, not even a “last viewed” timestamp written synchronously.
  • Return ETag so clients get free 304s.

POST

POST is the general-purpose method: “process this payload according to the resource's own semantics.” It is the correct choice for creating a child resource under a collection, and the correct escape hatch for actions that genuinely are not CRUD.

POST /v1/orders
Idempotency-Key: 7c9e-41ab

HTTP/1.1 201 Created
Location: /v1/orders/8812
Create returns 201 with a Location header pointing at the new resource.

Because POST is not idempotent, a client that times out cannot know whether the order was created. The fix is an idempotency key: the server stores the key with the result and replays the same response for a repeat. This is exactly what Stripe does, and it is the answer interviewers are listening for.

Actions that are not CRUD

POST /v1/orders/8812/cancel
POST /v1/invoices/91/send
POST /v1/videos/12/transcode   → 202 Accepted + Location: /jobs/55

Purists will argue for PATCH with a state field. In practice a named action endpoint is clearer, easier to authorise, and easier to rate limit. Use 202 Accepted when the work is asynchronous and hand back a job URL to poll.

PUT and PATCH

PUT replaces the resource at a URI with the payload — the payload is the complete new state. PATCH applies a partial modification. The difference has real consequences for concurrency and for retries, which is why it has its own deep dive in this series.

PUT /v1/users/42
{"name":"Ada","email":"ada@example.com","timezone":"UTC"}

PATCH /v1/users/42
{"timezone":"Europe/London"}
PUT is a full replacement — omitted fields are cleared.

PUT is idempotent because the final state does not depend on how many times you sent it. PATCH is idempotent only if the patch is absolute ({"qty": 5}) rather than relative ({"qtyDelta": +1}).

DELETE

Remove the resource. Return 204 No Content when there is nothing to say, or 200 with the deleted representation if the client needs it. A second DELETE returning 404 is fine — idempotency is about state, not status codes — though returning 204 again is friendlier for retry logic.

  • Soft deletes: still expose DELETE; the tombstone is an implementation detail.
  • Cascading deletes should be explicit — ?cascade=true or a dedicated endpoint.
  • Bulk delete has no standard; POST /v1/orders/bulk-delete is honest.

HEAD, OPTIONS, TRACE, CONNECT

  • HEAD — identical to GET but the response has no body. Useful for checking existence, Content-Length before a large download, or whether an ETag changed.
  • OPTIONS — what can I do here. In practice you meet it as the CORS preflight that browsers send before a cross-origin request with custom headers or a non-simple method.
  • TRACE — echoes the request back. Enables Cross-Site Tracing attacks. Turn it off.
  • CONNECT — asks a proxy to open a raw TCP tunnel. This is how HTTPS works through a forward proxy. You will never implement it in an application.
OPTIONS /v1/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET,POST,PATCH,DELETE
Access-Control-Allow-Headers: authorization,content-type
Access-Control-Max-Age: 600
A CORS preflight — the OPTIONS request you did not write.

Common mistakes

MistakeWhy it hurtsFix
Mutating on GETCrawlers and prefetchers trigger writesUse POST/PATCH/DELETE
POST for everything (RPC-over-HTTP)No caching, no safe retries, no semantics for proxiesModel resources; reserve POST for creates and actions
PUT for partial updatesOmitted fields silently wipedPATCH, or require the full body
200 for every responseClients must parse the body to detect failureUse the status code as the primary signal
DELETE with a required bodyProxies and some clients strip itPut parameters in the URL
Retrying POST blindlyDuplicate orders and double chargesIdempotency-Key with server-side dedupe

Interview framing

“Reads are GET so the CDN and the browser can cache them with ETags. Creates are POST with an idempotency key, returning 201 and a Location header, because POST is not idempotent and our clients retry on 5xx. Full replacements are PUT, field-level edits are PATCH with an If-Match ETag for optimistic concurrency, and deletes are DELETE returning 204 on repeat calls so retry logic stays simple. Non-CRUD operations get explicit action sub-resources rather than being smuggled into PATCH.”

Keep reading

Suggested next articles based on this one.

Design it, don't just read it.

Practise LLD and system design problems with structured rubrics and AI feedback.

Start practising free