Skip to content

HTTP status codes

Every HTTP response starts with a 3-digit status code that says how the request went. As a tester it’s often the first thing you assert — a correct status is the quickest signal that an endpoint behaved. This page is a reference: skim the classes, then keep the tables handy.

The first digit tells you the category:

RangeClassMeaning in one line
1xxInformational”Got it, still working.” (rare in tests)
2xxSuccess”It worked.”
3xxRedirection”Look somewhere else.”
4xxClient errorYou sent something wrong.”
5xxServer errorI broke.”

The two you assert most are 2xx (it worked the way you meant) and 4xx (the app correctly rejected bad input). A 5xx in a test usually means a real bug.

If you only memorise a handful, memorise these:

CodeNameWhen you see it
200OKA successful GET (or any request that returns data).
201CreatedA POST successfully created a resource.
204No ContentSuccess, but there’s no body to return (often a DELETE).
301 / 302Moved / FoundA redirect to another URL.
400Bad RequestThe input was malformed or failed validation.
401UnauthorizedYou’re not logged in / no valid credential.
403ForbiddenYou’re logged in, but not allowed to do this.
404Not FoundThe resource doesn’t exist.
409ConflictClashes with current state (e.g. duplicate email).
422Unprocessable EntitySyntactically fine, but semantically invalid.
500Internal Server ErrorThe server threw an unhandled error.
  • 401 Unauthorized = “Who are you?” — you’re not authenticated. Log in first.
  • 403 Forbidden = “I know who you are, and no.” — you’re authenticated but lack permission.

A logged-out user hitting an admin page gets 401; a logged-in customer hitting an admin-only API gets 403.

CodeNameNotes
200OKThe standard success. The body holds the result.
201CreatedA new resource was made; the body is usually the new resource, sometimes with a Location header.
202AcceptedThe request was accepted but processing happens later (async jobs).
204No ContentSuccess with an empty body. Common for DELETE and some PUTs.
CodeNameNotes
301Moved PermanentlyThe resource lives at a new URL for good — update your links.
302FoundA temporary redirect. After a form POST, apps often redirect here.
304Not ModifiedYour cached copy is still fresh; the server sent no body.
307 / 308Temporary / Permanent RedirectLike 302/301 but the method must not change.

Many browser/test clients follow redirects automatically, so you may land on the final page without noticing the 3xx in between. TestMarket Lab redirects after a successful login and after checkout, for example.

4xx — Client errors (you sent something wrong)

Section titled “4xx — Client errors (you sent something wrong)”
CodeNameNotes
400Bad RequestMalformed request or failed validation (e.g. missing required field).
401UnauthorizedNot authenticated — missing/invalid credentials.
403ForbiddenAuthenticated but not permitted.
404Not FoundNo such resource at this URL.
405Method Not AllowedThe URL exists, but not for this method (e.g. DELETE where only GET is allowed).
409ConflictClashes with current state — duplicate email, version conflict.
422Unprocessable EntityWell-formed but semantically invalid; common in validation-heavy APIs.
429Too Many RequestsRate-limited — you’ve sent too many requests too fast.
CodeNameNotes
500Internal Server ErrorAn unhandled exception on the server. In tests, usually a real bug to report.
502Bad GatewayA proxy/gateway got an invalid response from an upstream server.
503Service UnavailableThe server is down or overloaded (deploys, maintenance).
504Gateway TimeoutAn upstream server didn’t respond in time.

The practice app maps these cleanly — handy when you write assertions:

ActionStatus
GET /api/products200
POST /api/products (valid)201
POST /api/products (missing name/price)400
POST /api/auth/login (wrong password)401
POST /api/auth/register (email already exists)409
GET /api/products/99999 (no such id)404
POST /api/orders (empty items)400

Playwright’s response object exposes the status directly:

const res = await request.post('/api/products', {
data: { name: 'Keyboard', price: 49.99 },
});
expect(res.status()).toBe(201); // exact code
expect(res.ok()).toBeTruthy(); // true for any 2xx

Always assert the status and the body together — a broken endpoint can return 200 with an error message inside, and the status alone won’t catch that:

const res = await request.get('/api/products/1');
expect(res.status()).toBe(200);
const body = await res.json();
expect(body).toHaveProperty('id', 1);

This is exactly the pattern used throughout Module 9.