HTTP Methods Explained: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
HTTP methods are the verbs of an API: they tell the server what you want to do with a resource. Mix them up and you get bugs like a “GET” that deletes data or a retry that charges a customer twice. In this lesson you will learn all seven methods testers use, the difference between PUT and PATCH, what “safe” and “idempotent” really mean, and you will run each method on a live object you create yourself.
Real-Life Example: Managing Your Address Book in a Shopping App
{"is_default": true}. Removing it sends DELETE /addresses/7.This mapping of methods to actions is called CRUD: Create (POST), Read (GET), Update (PUT/PATCH) and Delete (DELETE).
All HTTP Methods at a Glance
| Method | Purpose | Has body? | Safe | Idempotent | Typical success code |
|---|---|---|---|---|---|
| GET | Read a resource or list | No | Yes | Yes | 200 OK |
| POST | Create a new resource / trigger an action | Yes | No | No | 201 Created (or 200/202) |
| PUT | Replace the whole resource, or create at a known id | Yes | No | Yes | 200 OK / 201 Created / 204 |
| PATCH | Partial update | Yes | No | Not guaranteed | 200 OK / 204 |
| DELETE | Remove a resource | Usually no | No | Yes | 204 No Content / 200 |
| HEAD | Same as GET but headers only | No | Yes | Yes | 200 OK |
| OPTIONS | Which methods are allowed (also CORS pre-flight) | No | Yes | Yes | 200 / 204 |
Two rarer methods: TRACE (echoes the request, should be disabled in production for security) and CONNECT (used by proxies for HTTPS tunnels).
GET: Read Data
GET retrieves data and must never change anything on the server. Parameters go in the URL path or query string.
https://sqatools.in/wp-json/sqa-api/v1/users?role=admin&sort=age&order=desccurl -i \
"https://sqatools.in/wp-json/sqa-api/v1/users?role=admin&sort=age&order=desc"Test ideas: correct data and count; filters and sorting; 404 for a missing id; 400 for an invalid id (/users/abc); calling it twice changes nothing; sensitive data not exposed; response time.
/unsubscribe?user=42 or /orders/9/cancel that changes data is dangerous: browsers prefetch links, crawlers follow them and caches replay them. Testers should flag any GET that modifies state.POST: Create a New Resource
POST sends data in the body; the server decides the new id. A correct create returns 201 Created and a Location header.
https://sqatools.in/wp-json/sqa-api/v1/objectscurl -i \
-X POST \
-H "Content-Type: application/json" \
-d '{"name":"Office address","data":{"city":"Pune","pin":"411001"},"ttl":300}' \
"https://sqatools.in/wp-json/sqa-api/v1/objects"Test ideas: 201 + Location; body echoes what you sent; required fields missing → 422; extra unknown fields; client trying to set the id (the practice API rejects it with 422); duplicate submission.
Idempotency-Key header: the same key returns the first result instead of creating a second payment. Testing duplicate POSTs is a must for any money-related API.PUT: Replace (or Create at a Known URL)
PUT sends the complete new version of a resource to its URL. Any field you leave out is removed or reset. If the resource does not exist, many APIs create it (called an upsert).
On the practice API you choose the id. The first PUT returns 201 Created; repeating the same PUT returns 200 OK with the same content, which is what idempotent means.
https://sqatools.in/wp-json/sqa-api/v1/objects/my-address-01curl -i \
-X PUT \
-H "Content-Type: application/json" \
-d '{"name":"Home address","data":{"city":"Mumbai","pin":"400001"},"ttl":300}' \
"https://sqatools.in/wp-json/sqa-api/v1/objects/my-address-01"Test ideas: full replacement (omitted fields disappear); name missing → 422 (PUT needs the full representation); id in body different from id in URL → 422; repeat the call → same state; version increments.
PATCH: Update Only Some Fields
PATCH sends only the fields that change. The practice API uses JSON Merge Patch rules: fields you send are merged in, and a field set to null is removed.
curl -i -X PATCH "https://sqatools.in/wp-json/sqa-api/v1/objects/my-address-01" \
-H "Content-Type: application/json" \
-d '{"data":{"pin":"400002"}}'
Result: city stays “Mumbai”, pin becomes “400002”, version goes up by 1, and the ETag changes from "v1" to "v2".
Test ideas: only sent fields change; null removes a key; empty body {} → 422 “nothing to update”; PATCH on a missing id → 404; read-only fields (id, created_at) cannot be patched.
PUT vs PATCH: The Most Asked Interview Question
| PUT | PATCH | |
|---|---|---|
| Sends | The entire resource | Only the changes |
| Missing fields | Removed or set to default | Left unchanged |
| Idempotent | Yes, always | Not guaranteed (e.g. {"op":"increment"}) |
| Can create | Yes, at a client-chosen URL | Usually no (404 if missing) |
| Real example | Saving the whole edit-profile form | Toggling “dark mode” or “default address” |
Example: the stored object is {"name":"Home","data":{"city":"Mumbai","pin":"400001"}}.
PUT {"name":"Home","data":{"pin":"400002"}}→cityis gone.PATCH {"data":{"pin":"400002"}}→cityis kept.
DELETE: Remove a Resource
DELETE removes the resource. The common success code is 204 No Content (empty body).
https://sqatools.in/wp-json/sqa-api/v1/objects/my-address-01curl -i \
-X DELETE \
"https://sqatools.in/wp-json/sqa-api/v1/objects/my-address-01"Press Send twice. The first call returns 204, the second 404. Is that still idempotent? Yes: idempotency is about the server state (the object is gone either way), not about the status code.
Test ideas: 204 then GET returns 404; deleting twice; deleting without permission → 403; deleting a resource that has children (an order with payments); soft delete vs hard delete; DELETE on read-only resources → 405 (try DELETE /products/101).
HEAD and OPTIONS: The Methods Testers Forget
HEAD
Returns exactly the headers a GET would return, but no body. Used to check if a resource exists, its size (Content-Length) or its version (ETag) without downloading it, for example before downloading a 2 GB file.
https://sqatools.in/wp-json/sqa-api/v1/users/1curl -I \
"https://sqatools.in/wp-json/sqa-api/v1/users/1"OPTIONS
Asks which methods are allowed. The answer comes in the Allow header. Browsers also send OPTIONS automatically as a CORS pre-flight before cross-origin PUT/DELETE requests.
https://sqatools.in/wp-json/sqa-api/v1/userscurl -i \
-X OPTIONS \
"https://sqatools.in/wp-json/sqa-api/v1/users"405 Method Not Allowed
When you use a method an endpoint does not support, the correct answer is 405 with an Allow header listing valid methods. On the practice API, POST /users returns: “This endpoint supports GET, HEAD only.” A 404 or 500 here would be a bug.
Safe vs Idempotent, Explained Simply
- Safe = read-only. Calling it does not change data. (GET, HEAD, OPTIONS)
- Idempotent = calling it once or 10 times leaves the server in the same state. (All safe methods + PUT + DELETE)
Why testers care: networks fail, and clients and gateways retry. Retrying idempotent requests is safe; retrying POST without an idempotency key can create duplicates.
All Methods in One Playwright Test
import { test, expect } from '@playwright/test';
const BASE = 'https://sqatools.in/wp-json/sqa-api/v1';
test('HTTP methods on one object', async ({ request }) => {
const id = `pw-${Date.now()}`;
const url = `${BASE}/objects/${id}`;
// PUT creates (201), PUT again is idempotent (200, same data)
const body = { name: 'Home', data: { city: 'Mumbai', pin: '400001' }, ttl: 120 };
expect((await request.put(url, { data: body })).status()).toBe(201);
expect((await request.put(url, { data: body })).status()).toBe(200);
// PATCH changes one field and keeps the rest
const patched = await (await request.patch(url, { data: { data: { pin: '400002' } } })).json();
expect(patched.data).toEqual({ city: 'Mumbai', pin: '400002' });
// HEAD: headers only, no body
const head = await request.head(url);
expect(head.status()).toBe(200);
expect((await head.body()).length).toBe(0);
// DELETE twice: 204 then 404 (state is the same: gone)
expect((await request.delete(url)).status()).toBe(204);
expect((await request.delete(url)).status()).toBe(404);
});
test('unsupported method returns 405 with Allow header', async ({ request }) => {
const res = await request.post(`${BASE}/users`, { data: {} });
expect(res.status()).toBe(405);
expect(res.headers()['allow']).toBeTruthy();
});
Interview Questions: HTTP Methods
What is the difference between PUT and PATCH?
PUT replaces the entire resource with the representation you send, so omitted fields are removed, and it is idempotent. PATCH applies a partial update, changing only the fields you send, and is not guaranteed to be idempotent.
What is the difference between GET and POST?
GET reads data, sends parameters in the URL, is safe, idempotent and cacheable. POST sends data in the body to create a resource or trigger processing, changes server state and is not idempotent. Sensitive data should never go in GET query strings because URLs are logged.
What does idempotent mean? Which methods are idempotent?
An idempotent request leaves the server in the same state whether it is sent once or many times. GET, HEAD, OPTIONS, PUT and DELETE are idempotent; POST is not and PATCH is not guaranteed to be.
DELETE returns 204 the first time and 404 the second. Is it still idempotent?
Yes. Idempotency refers to the server state, not the response code. After either call the resource does not exist.
What is the difference between PUT and POST?
POST creates a resource at a URL chosen by the server and is not idempotent. PUT creates or replaces a resource at a URL chosen by the client and is idempotent.
What is the use of the HEAD method?
HEAD returns the same headers as GET without the body. It is used to check existence, size, content type or ETag of a resource without downloading it.
What is the use of the OPTIONS method?
OPTIONS returns the allowed methods for a resource in the Allow header and is used by browsers as a CORS pre-flight request before cross-origin calls.
What status code should an API return for an unsupported method?
405 Method Not Allowed, with an Allow header listing the supported methods.
Can a GET request have a body?
The HTTP specification does not define meaning for a GET body, and many servers, proxies and tools ignore or reject it. APIs should use query parameters for GET; if you see an API requiring a GET body, flag it as a design risk.
How do you prevent duplicate orders when a POST is retried?
Use an Idempotency-Key header generated by the client for each logical operation; the server stores the first result for that key and returns it for retries. Testers verify that sending the same key twice creates only one order.