Start with the happy path, deliberately
Send the request exactly as documented, with valid data, and actually
read the full response — not just the status code. Confirm the response
headers (particularly Content-Type) match what's documented,
and that the body's structure matches what any consumer of the API would
expect to parse. This sounds obvious, but skipping straight to automation
without this manual pass is how a monitoring check ends up validating
something subtly different from what the API actually promises.
Then deliberately break things
An endpoint's error behavior is at least as important to verify as its success behavior, and it's the part most often left completely untested:
- Missing or invalid authentication — should return 401, not a 500 or a silent empty success response.
- Malformed request body — a missing required field or wrong data type should return a 400 with a body clear enough to debug from, not a generic error.
- A nonexistent resource ID — should return 404, not an empty 200, which looks successful but means something different ("this exists and has no data" vs. "this doesn't exist" are not the same answer).
- Rate limiting — if the API enforces limits, confirm
what actually happens at the boundary: a clean 429 with a
Retry-Afterheader is very different behavior from a silent drop or a generic 500.
Each of these is a case a real client will eventually hit in production — testing them manually once is far cheaper than discovering the actual behavior for the first time during an incident.
Check headers, not just the body
Response headers carry information the body doesn't: rate-limit remaining/reset values, caching directives, and CORS headers if the endpoint is meant to be called from a browser. An endpoint that's otherwise correct but missing the right CORS headers will work fine from a manual test tool while failing silently for every real browser-based client — a gap that's easy to miss unless headers are checked explicitly.
Test with realistic payload sizes, not just minimal examples
A documented example request is often trivially small. Testing with a payload closer to real-world size — a list of 500 items instead of 2, a realistic file upload size — can surface timeout behavior, pagination requirements, or size limits that a minimal example would never reveal. This is also where response-time expectations should actually be set: timing a minimal example gives an unrealistically fast number that won't hold up under real usage.
Only then decide what to automate
Manually working through all of the above surfaces exactly which behaviors are worth turning into an ongoing automated check — the ones that could plausibly regress silently — versus which were one-time verification that doesn't need to be repeated forever. Automating everything indiscriminately produces a large, slow check suite; automating based on what manual testing actually revealed as fragile produces a smaller, more useful one.