Lesson 1 of 9 · API Testing
HTTP Fundamentals
Requests, responses, methods, status codes and headers: the HTTP model every API tester needs, with examples you can try in the browser.
beginner 9 min read Updated
You will learn to
- Read and explain every part of an HTTP request and response
- Choose the right method and expected status code for an operation
- Explain safe and idempotent methods and why they matter for testing
- Use common headers for content negotiation, caching and authentication
Prerequisites
- None
Every API test you will ever write is, underneath, an HTTP request followed by checks on an HTTP response. Tools like Postman, REST Assured and Playwright’s request fixture are conveniences on top of that exchange. If you understand the exchange itself (methods, status codes, headers and bodies) you can test any HTTP API with any tool, read logs and browser network tabs with confidence, and write bug reports developers can act on immediately.
The authoritative definition of HTTP semantics is RFC 9110 (published in 2022), which replaced the older RFC 7231 family. This lesson follows its terminology.
The request–response model
HTTP is a request–response protocol. A client (browser, mobile app, test script) sends a request to a server; the server sends back exactly one response. Each exchange is stateless: the server does not inherently remember previous requests. Anything that must persist between requests, such as who you are, travels in every request, usually in a cookie or an Authorization header.
Anatomy of a request
Here is a complete HTTP/1.1 request to create a user:
POST /api/users?notify=true HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example
Content-Length: 58
{"name":"Ana Silva","email":"ana@example.com","role":"admin"}
| Part | Example | Purpose |
|---|---|---|
| Method | POST |
What you want to do |
| Target (path + query) | /api/users?notify=true |
Which resource, with optional parameters |
| Version | HTTP/1.1 |
Protocol version |
| Headers | Content-Type: application/json |
Metadata about the request and body |
| Blank line | Separates headers from body | |
| Body | {"name":"Ana Silva",...} |
The data being sent (optional) |
Combined with the scheme and host, the full URL is https://api.example.com/api/users?notify=true. Its parts:
- Scheme:
https(HTTP over TLS, which is encrypted) - Host:
api.example.com - Path:
/api/users - Query string:
?notify=true, a set ofkey=valuepairs separated by&
HTTP/2 and HTTP/3 change how these messages are encoded on the wire, but the semantics (methods, status codes, headers) are the same. Browser dev tools show them the same way.
Anatomy of a response
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/u_42
Cache-Control: no-store
Date: Mon, 09 Nov 2026 10:15:00 GMT
{"id":"u_42","name":"Ana Silva","email":"ana@example.com","role":"admin"}
- Status line: version, status code (
201) and reason phrase (Created). - Headers: metadata, including where the new resource lives (
Location). - Body: the representation of the result.
In API tests you typically assert on all three: the status code, important headers, and the body content and structure.
HTTP methods
| Method | Typical meaning | Has request body? | Safe | Idempotent |
|---|---|---|---|---|
GET |
Read a resource | No (not meaningful) | Yes | Yes |
HEAD |
Like GET but headers only | No | Yes | Yes |
POST |
Create, or trigger processing | Yes | No | No |
PUT |
Replace a resource entirely (or create at a known URL) | Yes | No | Yes |
PATCH |
Partially update a resource | Yes | No | No (not guaranteed) |
DELETE |
Remove a resource | Usually no | No | Yes |
OPTIONS |
Ask what the server supports (used in CORS preflight) | No | Yes | Yes |
Safe and idempotent: why testers care
- A safe method is read-only by definition: the client is not requesting any change on the server. A
GETthat deletes data is a serious defect, because crawlers, link previews and browser prefetching can trigger GETs automatically. - An idempotent method has the same intended effect on the server whether you send it once or many times. Sending
DELETE /users/42twice leaves user 42 deleted either way. SendingPUTwith the same body twice leaves the same final state.
Idempotency matters because networks fail and clients retry. Good tests for this:
- Send the same
PUTtwice and check the resource is identical and no duplicate is created. - Send the same
DELETEtwice. The second response may be404 Not Foundor204 No Contentdepending on the API’s design; check it matches the documented behaviour and that nothing else is deleted. - Send the same
POSTtwice and check what happens. Without an idempotency key, two orders are usually created; if the API supports anIdempotency-Keyheader, the second request should not create a duplicate.
Status codes
Status codes are three-digit numbers grouped by their first digit:
| Class | Meaning | Common codes |
|---|---|---|
| 1xx | Informational | 100 Continue, 103 Early Hints |
| 2xx | Success | 200 OK, 201 Created, 202 Accepted, 204 No Content |
| 3xx | Redirection | 301 Moved Permanently, 302 Found, 304 Not Modified, 307, 308 |
| 4xx | Client error: the request is wrong | 400, 401, 403, 404, 405, 409, 415, 422, 429 |
| 5xx | Server error: the server failed | 500, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout |
The client errors are where most API testing happens. Know these precisely:
| Code | Meaning | Example test |
|---|---|---|
400 Bad Request |
Malformed request, such as invalid JSON | Send {"name": (truncated JSON) |
401 Unauthorized |
Missing or invalid authentication credentials | Call without a token, or with an expired one |
403 Forbidden |
Authenticated, but not allowed | A viewer tries to delete a user |
404 Not Found |
Resource does not exist (or is hidden) | GET /users/does-not-exist |
405 Method Not Allowed |
Method not supported for this resource | DELETE /health |
409 Conflict |
Conflicts with current state | Register an email that already exists |
415 Unsupported Media Type |
Body format not accepted | Send XML to a JSON-only endpoint |
422 Unprocessable Content |
Well-formed but semantically invalid | Valid JSON with "age": -5 |
429 Too Many Requests |
Rate limit exceeded | Burst requests past the documented limit |
The names 401 Unauthorized and 403 Forbidden are historically confusing: 401 really means “unauthenticated”. A useful memory aid is “401: who are you? 403: I know who you are, and no.”
Look up any code quickly with the HTTP Status Code Reference.
Headers you will test
Headers are case-insensitive Name: value pairs. A handful matter in almost every API test:
| Header | Direction | What to check |
|---|---|---|
Content-Type |
Both | The format of the body, e.g. application/json. Responses should declare it accurately. |
Accept |
Request | Formats the client can handle. Sending Accept: application/xml to a JSON-only API should give 406 Not Acceptable or a documented fallback. |
Authorization |
Request | Credentials, e.g. Bearer <token> or Basic <base64>. |
Location |
Response | URL of a newly created resource after 201, or the redirect target for 3xx. |
Cache-Control |
Both | Caching rules. Responses with personal data should typically be no-store or private. |
ETag / If-None-Match |
Both | Conditional requests; a matching ETag should yield 304 Not Modified. |
Set-Cookie / Cookie |
Response / Request | Session cookies; check HttpOnly, Secure and SameSite attributes. |
Retry-After |
Response | How long to wait after a 429 or 503. |
The HTTP Headers Reference explains many more.
Try it yourself
You do not need any tools to see real HTTP. Open your browser’s developer tools (F12), go to the Network tab, and reload any page. Click a request to see its method, URL, status, request headers, response headers and body.
From a terminal, curl shows the raw exchange:
# -i includes response headers in the output
curl -i https://jsonplaceholder.typicode.com/posts/1
# POST JSON, showing the full request and response with -v
curl -v -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title":"hello","body":"from curl","userId":1}'
jsonplaceholder.typicode.com is a free fake API: it returns realistic responses (including 201 Created for the POST) but does not actually persist your data. You can build commands like these visually with the cURL Generator.
And the same request in JavaScript (Node 22 or the browser console), with checks you would make in a test:
const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify({ title: 'hello', body: 'from fetch', userId: 1 }),
});
console.log(res.status); // 201
console.log(res.headers.get('content-type')); // application/json; charset=utf-8
const body = await res.json();
console.log(body.id, body.title); // 101 hello
Practice exercise
Open the API Playground (or use curl against a test API you are allowed to call) and, for a single resource such as users, send requests that produce at least five different status codes. For each request, record:
- Method and URL
- Relevant request headers and body
- Status code received
- Whether that status code is correct according to RFC 9110 semantics, and why
A good result covers at least one 2xx, one 401 or 403, one 404, and one validation error (400 or 422), and explains any response that looks wrong as a potential defect.
Show a model answer
Your exact endpoints will differ; this is the shape of a strong answer.
| # | Request | Status | Correct? |
|---|---|---|---|
| 1 | GET /users/1 with a valid token |
200 | Yes: resource exists and is readable |
| 2 | POST /users with valid JSON body |
201 | Yes, and a Location header points to the new user |
| 3 | GET /users/1 with no Authorization header |
401 | Yes: no credentials supplied |
| 4 | DELETE /users/1 as a viewer role |
403 | Yes: authenticated but not permitted |
| 5 | GET /users/999999 |
404 | Yes: no such user |
| 6 | POST /users with "email": "not-an-email" |
422 | Yes: well-formed JSON, invalid content (400 is also commonly used; check the API docs) |
| 7 | POST /users with a truncated JSON body |
500 | No. This is a defect: malformed input should return 400 with an error message |
The last row shows the mindset: you are not just recording codes, you are judging whether each one is the right answer.
Key takeaways
- An HTTP exchange is a request (method, target, headers, optional body) and a response (status code, headers, optional body). RFC 9110 defines the semantics.
- Safe methods do not request changes; idempotent methods have the same effect when repeated. Test both properties.
- Know the 4xx codes precisely, especially 401 vs 403 and 400 vs 422.
- Any request that triggers a 5xx is a bug worth reporting.
- Use browser dev tools and
curl -i/curl -vto see real HTTP traffic before reaching for heavier tools.
Key terms
Finished this lesson?
Progress is saved in this browser. No account needed.