HTTP Status Codes: Complete List with API Testing Examples

HTTP Status Codes: Complete List with API Testing Examples

The status code is the first thing every client reads in an API response, and the first thing a tester should assert. A login API that answers 200 OK with “invalid password” in the body, or a typo in a search that crashes with 500, are real bugs that ship to production every day. This lesson is your complete HTTP status codes reference for API testing: every code you will meet, what it means in real life, and how to test it on live endpoints.

Quick answerHTTP status codes are 3-digit numbers grouped into five classes: 1xx informational, 2xx success (200 OK, 201 Created, 204 No Content), 3xx redirection (301, 302, 304), 4xx client errors (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Content, 429 Too Many Requests) and 5xx server errors (500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout). In API testing you verify that every scenario returns the correct, most specific code.
Advertisement

The 5 Classes of HTTP Status Codes

ClassMeaningReal-life analogy (courier delivery)Who should act
1xx InformationalRequest received, continue“Parcel picked up, in process”Nobody, usually invisible
2xx SuccessIt worked“Delivered”Client uses the result
3xx RedirectionLook somewhere else“Customer moved, forwarded to new address”Client follows the new location
4xx Client errorYour request is wrong“Incomplete address” or “no one authorised to receive”Client must fix the request
5xx Server errorServer failed“Our van broke down”Server team must fix; client may retry
The golden rule for testers A 4xx means “the client did something wrong”, a 5xx means “the server is broken”. So any 5xx caused by user input is a bug. Invalid input must produce a 4xx with a helpful message.

Try Any Status Code Live

The practice API has a helper endpoint that returns whatever status you ask for: GET /status/{code}. Use it to check how your client, Postman tests or Playwright framework handle each code. Here is a 503 with a Retry-After header:

GEThttps://sqatools.in/wp-json/sqa-api/v1/status/503
curl -i \
  "https://sqatools.in/wp-json/sqa-api/v1/status/503"

Edit the URL to /status/201, /status/204, /status/409 and more. An invalid code such as /status/700 returns 400.

Advertisement

2xx Success Codes

CodeNameWhen to expect itWhat to assert
200OKSuccessful GET, PUT, PATCH; POST actions that don’t create (e.g. login, search)Body contains the expected data
201CreatedPOST (or PUT) created a new resourceLocation header + new id in the body
202AcceptedAsync work queued (report export, bulk upload)Job id; poll until done
204No ContentDELETE, or update with nothing to returnBody is truly empty
206Partial ContentRange requests (video streaming, resumable downloads)Content-Range header

200 vs 201 vs 204

  • Create an order → 201 Created (not 200). The practice API’s POST /objects follows this rule.
  • Fetch an order → 200 OK.
  • Delete an order → 204 No Content.
Classic bug: “200 with an error inside” HTTP 200 with body {"success": false, "message": "Invalid OTP"}. Monitoring sees no errors, caches may store the failure, and clients that only check status think it worked. Report it: the status should be a 4xx.

3xx Redirection Codes

CodeNameReal exampleKeeps method and body?
301Moved PermanentlyOld API v1 URL permanently moved; SEO-safe redirectNot guaranteed (clients may switch POST to GET)
302Found (temporary)Login redirect to SSO pageNot guaranteed
303See OtherAfter POST payment, redirect to the receipt pageNo, always becomes GET
304Not ModifiedCached product data still valid (ETag matched)No body sent
307Temporary RedirectRequest moved to another server during maintenanceYes
308Permanent RedirectPermanent move that must keep POST as POSTYes

Practice: GET /redirect returns 302; POST /redirect?status=307 keeps the POST; POST /redirect?status=303 switches to GET. Conditional GET: request /products/101 with If-None-Match set to its ETag and you get 304. Details in HTTP headers and caching.

Advertisement

4xx Client Error Codes

CodeNameMeaningReal-life example
400Bad RequestMalformed request: bad JSON, invalid parameter format/users/abc when id must be a number
401UnauthorizedNot authenticated: missing, invalid or expired credentialsSession expired in the banking app
403ForbiddenAuthenticated, but not allowedViewer tries to delete a record
404Not FoundResource doesn’t exist (or is hidden from you)Order id 999 doesn’t exist
405Method Not AllowedMethod not supported on this URLDELETE /products/101
406Not AcceptableServer can’t produce the format in AcceptAsking for CSV from a JSON-only API
408Request TimeoutClient took too long to send the requestVery slow upload
409ConflictConflicts with current stateUsername already taken; seat already booked
410GonePermanently removedRetired API version
412Precondition FailedIf-Match version is staleTwo agents editing the same ticket
413Content Too LargeBody over the limitUploading a 50 MB profile photo
415Unsupported Media TypeWrong Content-TypeSending text/plain to a JSON API
422Unprocessable ContentWell-formed but fails validation rulesAge 17 when minimum is 18
429Too Many RequestsRate limit exceededOTP requested 6 times in a minute
Advertisement

The Confusing Pairs (Interview Favourites)

401 Unauthorized vs 403 Forbidden

Office building analogy No ID card at the gate → the guard stops you: 401 (“who are you?”). Valid ID card, but you try to enter the server room you have no access to → 403 (“I know who you are, but you are not allowed”).

A 401 should include a WWW-Authenticate header. Try it on the Basic auth endpoint with no credentials:

GEThttps://sqatools.in/wp-json/sqa-api/v1/basic/profile
curl -i \
  "https://sqatools.in/wp-json/sqa-api/v1/basic/profile"

On the practice API, a missing API key gives 401, a wrong API key gives 403, and a viewer token trying to create gives 403.

400 Bad Request vs 422 Unprocessable Content

  • 400: the server can’t even read it. Broken JSON, empty body, a JSON array instead of an object, text where a number is required in the URL.
  • 422: the JSON is valid, but the values break business rules: invalid email, age out of range, unknown enum value.
POSThttps://sqatools.in/wp-json/sqa-api/v1/validate/user
curl -i \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"name":"A","email":"not-an-email","age":17}' \
  "https://sqatools.in/wp-json/sqa-api/v1/validate/user"

The response lists every failing field at once, which is the behaviour you want from a well-designed API.

404 Not Found vs 403 for Hidden Resources

For security, many APIs return 404 instead of 403 when you request another user’s private resource, so attackers can’t even learn that it exists. Both are acceptable; returning the data is not.

409 Conflict vs 412 Precondition Failed

409 is a business conflict (duplicate email). 412 is a technical version conflict when you send If-Match with an old ETag (optimistic locking). The practice API’s /objects supports both scenarios.

Advertisement

5xx Server Error Codes

CodeNameTypical causeWhat testers do
500Internal Server ErrorUnhandled exception, null pointer, bad queryAlways a bug; attach request + trace id
501Not ImplementedFeature/method not built yetCheck against requirements
502Bad GatewayGateway got an invalid reply from the service behind itCheck service health and logs
503Service UnavailableMaintenance or overload; should send Retry-AfterVerify clients back off and retry
504Gateway TimeoutService behind the gateway too slowPerformance investigation
Security check on 5xx Error responses must never contain stack traces, SQL queries, file paths or framework versions. Attackers use these details. Always read the body of a 500 during testing.

Status Codes for Each HTTP Method

MethodSuccessCommon errors to test
GET200 (304 if cached)400, 401, 403, 404
POST201 (200 for actions, 202 async)400, 401, 403, 409, 413, 415, 422, 429
PUT200 / 201 (upsert) / 204400, 404, 409, 412, 422
PATCH200 / 204400, 404, 412, 422
DELETE204 / 200401, 403, 404, 409
Advertisement

Automating Status Code Checks with Playwright

A data-driven test checks many codes in a few lines. Use maxRedirects: 0 to assert a redirect itself instead of following it.

import { test, expect } from '@playwright/test';

const BASE = 'https://sqatools.in/wp-json/sqa-api/v1';

const cases = [
  { name: 'existing user',      path: '/users/1',     expected: 200 },
  { name: 'missing user',       path: '/users/999',   expected: 404 },
  { name: 'non-numeric id',     path: '/users/abc',   expected: 400 },
  { name: 'page size too big',  path: '/users?per_page=51', expected: 400 },
  { name: 'no credentials',     path: '/basic/profile',     expected: 401 },
  { name: 'service down',       path: '/status/503',  expected: 503 },
];

for (const c of cases) {
  test(`GET ${c.path} -> ${c.expected} (${c.name})`, async ({ request }) => {
    const res = await request.get(`${BASE}${c.path}`);
    expect(res.status()).toBe(c.expected);
  });
}

test('redirect is 302 when not followed', async ({ request }) => {
  const res = await request.get(`${BASE}/redirect`, { maxRedirects: 0 });
  expect(res.status()).toBe(302);
  expect(res.headers()['location']).toBeTruthy();
});

test('503 tells the client when to retry', async ({ request }) => {
  const res = await request.get(`${BASE}/status/503`);
  expect(res.headers()['retry-after']).toBe('10');
});

Note: res.ok() is true only for 200-299. Playwright does not throw on 4xx/5xx by default, which is what you want for negative tests.

How to Report a Wrong Status Code

A good bug title: “POST /orders returns 500 instead of 422 when quantity is negative”. Include the full request (cURL), the actual and expected status, the response body, the trace or request id header and the environment. Developers fix these fast when they can replay the request.

Advertisement

Interview Questions: HTTP Status Codes

What is the difference between 401 and 403?

401 Unauthorized means the request is not authenticated: credentials are missing, invalid or expired, and the client should log in. 403 Forbidden means the client is authenticated but does not have permission for this action or resource.

What is the difference between 400 and 422?

400 Bad Request means the server cannot parse or understand the request, such as malformed JSON or an invalid parameter format. 422 Unprocessable Content means the request is well-formed but fails business validation, such as an invalid email or an out-of-range age.

Which status code should a successful POST return?

201 Created when a new resource is created, with a Location header pointing to it. 200 OK is acceptable for POST actions that don’t create a resource (login, search), and 202 Accepted for asynchronous processing.

What is the difference between 200 and 204?

Both mean success. 200 OK returns a body; 204 No Content returns no body, commonly used for DELETE or updates where nothing needs to be returned.

What is the difference between 301 and 302?

301 is a permanent redirect: clients and search engines should update to the new URL. 302 is temporary: keep using the original URL. Use 307/308 when the method and body must be preserved.

When does an API return 304 Not Modified?

When the client sends a conditional request (If-None-Match with an ETag or If-Modified-Since) and the resource has not changed. The server returns 304 with no body, and the client uses its cached copy.

What is the difference between 502, 503 and 504?

502 Bad Gateway: the gateway received an invalid response from the upstream service. 503 Service Unavailable: the service is down or overloaded, ideally with Retry-After. 504 Gateway Timeout: the upstream service did not respond in time.

Is a 500 error always a bug?

From a tester’s point of view, yes. A 500 means an unhandled server failure. If it is caused by client input, it should have been a 4xx validation error; if it is caused by an internal problem, it needs fixing and should not expose stack traces.

What status code is returned when rate limits are exceeded?

429 Too Many Requests, ideally with a Retry-After header and rate limit headers such as X-RateLimit-Limit and X-RateLimit-Remaining.

An API returns 200 with an error message in the body. What do you do?

Log it as a defect: the status code should reflect the result (for example 400, 401 or 422). Explain the impact: monitoring misses failures, clients treat errors as success and caches may store failed responses.

What is 409 Conflict used for?

When a request conflicts with the current state of the resource, such as registering an email that already exists, booking a seat that was just taken or deleting a record that other data depends on.

Advertisement
View the full API testing syllabus