Back to Blog
On this page13 sections

// tutorial

The 12 API bugs I check for first

qa.codesqa.codes · 13 June 2026 · 9 min read
IntermediateAPI testersManual QA
api-testingchecklistbugs

Before I write a single automated API check, I run through these twelve by hand. They catch more real bugs than any suite I have shipped.

part ofAPI bugs QA should catch

The thing about API bugs is that they're boring and they're everywhere. The same dozen mistakes show up in every service I've tested, regardless of language, framework, or how senior the team is. So I stopped treating each API as a fresh mystery and built a first pass: twelve checks I run before I do anything clever. Most of the real defects are caught right here, before automation, before the UI is even wired up.

Here they are, in the order I run them.

1. Status codes that lie

The first thing I check is whether the status code matches reality. A 200 OK wrapping {"error": "not found"} in the body is the most common API bug there is — and the most dangerous, because every happy-path test passes. A created resource should be 201, not 200. A missing one is 404, not 200-with-empty-array-and-a-message. If the status line and the body disagree, the status line is usually the bug.

2. Validation that isn't there

I send the request the frontend would never send: missing required fields, a string where a number goes, a 10,000-character name, a negative quantity, an empty array, null where an object is expected. A well-built API rejects these with a 400 and a clear message. A fragile one either 500s (validation gap → unhandled exception) or — worse — accepts them and writes garbage to the database. The negative-number and empty-string cases catch real bugs astonishingly often.

3. Auth that can be skipped

Two checks. First: call the protected endpoint with no token. Does it 401, or does it quietly return data? Second: call it with a valid but wrong token — a different user's. Does it 403, or does it hand me someone else's record? That second case is broken object-level authorization, and it's the bug class that leaks customer data. You don't need exploit tooling to find it — just two accounts.

4. Pagination that doesn't add up

I request page 1, then page 2, and check three things: do any records repeat across pages, are any silently skipped, and does the total count actually match the number of records you can page through? Off-by-one in the offset is the classic. So is a nextPage link that 404s on the last page, or a page size the server silently caps without telling you.

5. Filtering that leaks

I filter by something restrictive — status=archived, ownerId=me — and check the result actually matches. Then I check the inverse leak: does ownerId=me quietly return everyone's records when the filter is misspelled or unsupported? An unknown filter parameter should be rejected or clearly ignored, never silently treated as "return everything." That silent-everything behaviour is both a correctness bug and a data-exposure bug.

6. Sorting that isn't stable

I sort by a field with duplicate values — sort=createdDate where ten rows share a date — and request the same page twice. If the order shifts between identical requests, the sort is unstable, and any pagination layered on top of it will duplicate and drop rows non-deterministically. This one looks flaky and is a real bug; it's not your test.

7. Duplicate creation

I POST the same "create" request twice, fast. Do I get two resources where there should be one? Double-submitted forms, retried requests, and impatient users all trigger this in production. The API should either dedupe on a natural key or accept an idempotency key — which is the next one.

8. Idempotency that's missing

For anything that charges money or sends a message, I check whether retrying is safe. Same request, same idempotency key, sent twice → one charge, one email, 200 both times. No idempotency support on a payment endpoint is a Sev1 waiting for a network blip. This is the check most teams skip and most regret.

9. Race conditions on shared state

I fire two requests at the same record at once — two "claim this voucher", two "book the last seat". Do both succeed when only one should? Last-write-wins overwrites, double-spends, and inventory going negative all live here. Hard to catch by hand reliably, but even a couple of concurrent requests surfaces the obvious ones.

10. Timezones and dates

I send a timestamp in one timezone and read it back. Does it come back as the same instant, or has it silently shifted? I check what happens at the boundaries: a date with no time, a 2025-02-29 (not a leap year), a daylight-saving transition. Date handling is where "works on my machine" goes to die — the glossary on timezone and date terms is worth a skim before you test anything time-sensitive.

11. Rate limits — present and sane

I hammer the endpoint and check two things: is there a limit at all (no limit = trivial denial of service), and when I hit it, do I get a clean 429 with a Retry-After, or a 500? Then the subtle one: does the limit reset correctly, or am I locked out longer than the headers claim?

12. Error responses that don't match

Finally, I collect the error responses from all the checks above and look at them together. Is 400 shaped the same as 404 and 409? One endpoint returning {"error": "..."} and the next returning {"message": "...", "code": 4001} means every consumer needs two parsers, and one of them will be wrong. Consistency in the error contract is a real, shippable defect — write it up.

Where this fits

This is a first pass, run by hand, ideally before the UI exists — it's the most concrete form of shift-left testing I know. Once you've found the bugs, the keepers become your automated suite. If you want a structured version to work from, the API testing checklist covers these in more depth, and the tools directory compares Postman, Bruno, REST Assured, and Karate if you're deciding what to automate them in.

The 12-bug API first pass

  • Status code matches the body (no 200-wrapped errors)
  • Validation rejects missing / wrong-type / out-of-range input with a 400
  • No token → 401; wrong user's token → 403 (not someone else's data)
  • Pagination has no repeats, no skips, and a correct total
  • Filters match exactly; an unknown filter never means "return everything"
  • Sorting on duplicate values is stable across identical requests
  • Double POST doesn't create two resources
  • Retried payments / messages are idempotent
  • Concurrent writes to one record don't double-spend
  • Timestamps round-trip; leap days and DST boundaries hold
  • Rate limit exists, returns 429 + Retry-After, and resets correctly
  • Error responses share one consistent shape across endpoints

// RELATED QA.CODES RESOURCES


// related

Tutorials·13 June 2026 · 8 min read

API pagination, filtering, and sorting bugs

The specific bugs that hide in paginated, filtered, and sorted endpoints — off-by-one pages, unstable sorts, and filter leaks.

api-testingpaginationbugs