beginner·9 min read·Updated Jul 16, 2026

HTTP Methods Explained: GET, POST, PUT, PATCH, DELETE, QUERY

GET, POST, PUT, PATCH, DELETE — the five verbs that carry 99% of API traffic — plus QUERY, the emerging method for safe reads with a body. Here's what each one means, with runnable examples.

The verbs that matter

HTTP defines several request methods, but five of them do 99% of the work — with a sixth, QUERY, now emerging to fill a real gap:

MethodPurposeHas body?Idempotent?Safe?
GETRead dataNoYesYes
POSTCreate a resource (or non-idempotent action)YesNoNo
PUTReplace a resource fullyYesYesNo
PATCHUpdate a resource partiallyYesNo (usually)No
DELETERemove a resourceRarelyYesNo
QUERYRead data using a request bodyYesYesYes

Two terms that trip everyone up:

  • Safe — doesn't change server state. GET is safe; POST is not.
  • Idempotent — calling it N times has the same effect as calling it once. Deleting the same user twice should not crash; it should either succeed both times or return "already deleted." POST is typically not idempotent — calling it twice creates two resources.

Understanding safety and idempotency isn't academic. It determines whether it's safe to retry a request after a network timeout. You can safely retry GET, PUT, DELETE, and QUERY. Retrying POST can create duplicates.

POST — create something

Click Run below. You're sending a POST to /api/v1/users with a JSON body. The server creates a new user and responds with 201 Created plus the full user record including a server-generated id and created_at timestamp.

POST/api/v1/users
Create a new user with POST.
curl -X POST 'https://demo.totalshiftleft.ai/api/v1/users' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com","role":"user"}'

Notice three things in the response:

  1. Status 201 — not 200. 201 Created specifically signals that a new resource was created.
  2. The id field — the server assigns UUIDs. Never trust the client to assign primary keys.
  3. Full record echoed back — this saves you a follow-up GET to see what was actually stored.

GET — read something

GET/api/v1/users
List all users with GET — safe and idempotent.
curl -X GET 'https://demo.totalshiftleft.ai/api/v1/users'

This returns the user you just created, plus any others in your sandbox session. GET should never change server state. If you build an API where GET /users/123/delete actually deletes the user, you'll be haunted by search-engine crawlers nuking your database — a real bug that has shipped to production more than once.

PUT vs PATCH — the eternal confusion

Both update an existing resource. The difference:

  • PUT replaces the entire resource. If you PUT { "name": "Alice Smith" } to a user record, any fields you didn't include (email, role) may be cleared or reset to defaults. PUT says: "here is the new full state."
  • PATCH modifies only the fields you send. PATCH { "role": "admin" } changes just the role and leaves everything else alone.

In practice, most REST APIs in 2026 use PATCH for updates because partial updates are more common and less error-prone. Use PUT when you genuinely want to replace a resource wholesale — for example, replacing a user's entire address book.

We cover PUT vs PATCH in depth in a dedicated lesson.

DELETE — remove something

DELETE is simple but has a quirky response convention: success returns 204 No Content — a 2xx success status with no response body. You didn't ask for data, you asked for an action; the server completed it, there's nothing to send back.

Testers new to REST sometimes flag 204 as a bug because "where's the JSON?" It's correct behavior.

QUERY — read something, but with a body

QUERY is the newest method here. It's specified in an IETF draft (draft-ietf-httpbis-safe-method-w-body) and solves a problem developers have worked around for years: how do you send a complex search without a request body?

GET is the natural verb for reading data, but GET requests aren't supposed to carry a body — so any filter has to be crammed into the URL as query-string parameters. That falls apart when a search is large or structured: a faceted product filter, a GraphQL-style selection, or a geospatial polygon can easily blow past URL length limits (browsers and proxies often cap around 2,000–8,000 characters) and become impossible to read in logs.

The common workaround is POST /search. It works, but it's a lie: POST tells every cache, proxy, and retry layer that the request is unsafe and non-idempotent, so nothing can cache the result and automatic retries are risky — even though a search changes nothing on the server.

QUERY fixes this. It carries a request body like POST, but it is safe and idempotent like GET:

QUERY /api/v1/users HTTP/1.1
Content-Type: application/json

{
  "filter": { "role": "admin", "status": "active" },
  "sort": [{ "field": "created_at", "order": "desc" }],
  "limit": 50
}

The server treats the body as the query definition and returns the matching results. Because QUERY is safe and idempotent, the response can be cached (the spec allows caches to key on the request body) and the request can be retried after a timeout without side effects.

How it differs from the others:

  • vs GET — same read-only, cacheable, retry-safe semantics, but the query lives in the body instead of the URL. Use QUERY when the query is too big or too structured for a query string.
  • vs POST /search — same "body carries the query" shape, but QUERY correctly advertises that the call is safe and idempotent, so infrastructure can cache and retry it.

A note on status. As of 2026, QUERY is still a standards-track draft, not a finalized RFC. Support is uneven: some frameworks and API gateways recognize it, many don't, and some intermediaries may reject unknown methods. For public-facing APIs, POST /search is still the pragmatic default — but QUERY is worth knowing, and worth testing for if your stack supports it, because it's the correct semantic answer to "a read that needs a body."

If your test tooling lets you send arbitrary methods, add a QUERY case wherever you currently POST to a search endpoint — you'll want it in place the moment your platform adopts the method.

Common mistakes

1. Using POST for everything. Some teams treat POST as the only verb and encode intent in URL paths like /users/create or /users/delete. This works but loses REST's self-documenting nature. A properly-designed API lets you guess what will happen from the method + URL alone.

2. Returning 200 for errors. "200 with success: false in the body" is an antipattern. The HTTP status code is the first line of error communication — tools, proxies, and retry logic depend on it. Use 4xx for client errors, 5xx for server errors.

3. Forgetting idempotency in retries. If your test framework retries failed POSTs, you'll get duplicate records. Either build idempotency keys into your API, or only retry GET/PUT/DELETE automatically.

Why testers care

Every API test is built around one of these methods. Your test matrix at minimum should cover: happy path for each CRUD verb, 404 when resource doesn't exist, 400 when request is malformed, and 401/403 when auth fails. If you use OpenAPI and a tool like ShiftLeft, this matrix is generated for you — but you still need to understand what's being tested.

What's next

Methods tell the server what you want. Status codes tell you what happened. Next lesson: HTTP status codes explained — why 2xx means success, 4xx means you messed up, and 5xx means they messed up.

Related lessons

Read more on the blog