API status codes testers should actually care about
You don't need all sixty-odd HTTP status codes. You need the dozen that carry real meaning for a tester — and the judgement to spot when the code is lying.
part ofAPI bugs QA should catchStatus codes are the first thing an API tells you and the first thing teams get wrong. You don't need to memorise the full registry — most of it never shows up in real testing. You need a working grasp of the dozen that matter, what each one promises, and the instinct to notice when the status line and the response body disagree. This is the deep dive behind the first item in the 12 API bugs I check first.
The mental model: the code is a promise
A status code is the server promising what kind of response this is, before you read the body. 2xx says "it worked." 4xx says "you got it wrong." 5xx says "I got it wrong." The whole skill is checking that the promise matches reality — that a "success" actually succeeded and a "client error" was actually your fault.
The success codes that aren't interchangeable
- 200 OK — generic success. Fine for reads.
- 201 Created — a resource was created. A
POSTthat makes something should return201(often with aLocationheader pointing at it), not200. Returning200from a create blurs "made it" and "found it." - 202 Accepted — "I've taken the request but haven't finished." For async work. Testing a
202as if the work is done is a classic mistake — the job might still fail later. - 204 No Content — success, deliberately empty body (common for
DELETEor an update with nothing to return). A204with a body is a contradiction; a test expecting JSON from a204will break.
The client errors that get confused
- 400 Bad Request — malformed/invalid input. The catch-all for validation failures.
- 401 Unauthorized — actually means unauthenticated: "I don't know who you are." Missing or bad credentials.
- 403 Forbidden — "I know who you are, and you're not allowed." The authorization code.
- The 401 vs 403 test: no token should give
401; a valid token for the wrong resource should give403. Swapping these is a real bug — and a403where it should be401(or vice versa) can leak whether a resource exists. - 404 Not Found — missing resource. Sometimes used deliberately instead of
403to avoid confirming a record exists. - 409 Conflict — the request collides with current state (duplicate create, edit conflict, already-refunded order). Often missing, so duplicates slip through as
200. - 422 Unprocessable Entity — syntactically valid but semantically wrong (well-formed JSON, but the values violate a rule). Some APIs use
400for everything; consistency matters more than which they pick. - 429 Too Many Requests — rate limited. Should come with
Retry-After, not a500.
The server errors
- 500 Internal Server Error — the server broke. For a tester this is gold: a
500from invalid input means a validation gap (an unhandled exception), which is a bug even though "the input was wrong." Bad input should be a4xx, never a5xx. - 502 / 503 / 504 — upstream/gateway/timeout problems. Worth distinguishing when testing resilience, but
500is the one you'll log most.
The judgement: when the code lies
The highest-value skill isn't the table above — it's noticing the mismatch between the status line and the body:
200 OKwrapping{"error": "..."}— the single most common API bug. Every happy-path test passes; nothing actually worked.200on a failed create (no resource made).500on bad input (should be400).404where403was meant (or the reverse), leaking existence.200with an empty body where the contract promised data.
Always assert the code and the body together. A test that checks only status === 200 is half a test.
Where this fits
Status codes are the foundation of API testing; get them right and the rest of the 12 API bugs and list-endpoint traps read more clearly. The API testing checklist and the tools directory cover putting these assertions into a suite.
Status-code checks
POSTthat creates returns201(not200);DELETE/empty-update returns204- Async work returns
202— don't assert completion on it - No token →
401; valid token, wrong resource →403 - Duplicates/conflicts return
409, not a silent200 - Invalid input returns
4xx(400/422) — never a500 - Rate limiting returns
429+Retry-After - Every assertion checks the status line AND the body together
// RELATED QA.CODES RESOURCES
Checklist
// related
The API test data problem nobody plans for
API suites fail on shared, stale, order-dependent data more than on wrong assertions. The own-your-data strategy — independent, unique, cleaned — that keeps them reliable.
Contract testing, explained without the Pact marketing
Contract testing is two things wearing one name: a model and a tool. The model is genuinely useful; the marketing for the tool oversells where it fits. Here's the model, separated from any vendor's pitch.