API Negative Testing: Breaking Your API Before Attackers Do
Happy paths prove your API works. Negative paths prove it fails safely. Here are the seven categories, a worked example, and where AI generation helps.
This lesson is part of API 101, our free beginner course. It assumes you know HTTP status codes and have run a first happy-path request.
What "negative testing" actually means
Negative testing sends deliberately wrong, malformed, malicious, or adversarial inputs to an API and asserts that the server responds safely — with a clean error, not a crash, a leak, or an unintended success.
It's the opposite of happy-path testing. Happy-path asks "does this work?" Negative asks "does this refuse to work when it should?"
Why most test suites under-cover this
Three reasons:
- Happy paths are easier to imagine. "Create user with valid data" is obvious; "create user with a 1 MB name and a surrogate-pair emoji" is not.
- Negative paths multiply quickly. One happy path per endpoint + 20 negative paths per field × 10 fields = 200 cases per endpoint.
- Negative paths feel less valuable to stakeholders. Nobody demos "we return a clean 400 when you send a null."
The fix for (2) is generation: you describe the schema once, and a tool produces the cases. More on that below.
The seven categories of negative tests
1. Malformed syntax
- Invalid JSON (trailing comma, unclosed brace).
- Wrong content-type (send JSON with
Content-Type: text/plain). - UTF-8 BOM in the body.
- Gibberish bytes.
- Empty body on a POST that requires one.
Expected: 400 with a clean error, not 500 or a stack trace.
2. Type abuse
- Send a string where the schema expects an int.
- Send
nullfor a required field. - Send an object for a scalar field.
- Send an array for a scalar field (
[1,2]for acountfield).
Expected: 400 with a field-level error.
3. Boundary abuse
- Empty string for a required string.
- Negative number for a field documented as positive.
9999999999999999999for an int field (overflow).- A 10 MB string body.
- A 10,000-element array.
Expected: 400, 413 (Payload Too Large), or graceful truncation — documented, not crashing.
4. Encoding and character abuse
- Null bytes in strings (
"abc\x00def"). - Control characters.
- Emoji and surrogate pairs.
- Right-to-left override (RLO) characters.
- SQL-like payloads (
' OR '1'='1). - Path traversal (
../../etc/passwd). - Command injection (
$(whoami)). - XSS payloads (
<script>alert(1)</script>).
Expected: accepted and escaped, or 400. Never executed.
5. Auth abuse
- Missing auth header.
- Malformed token (three dots, random base64).
- Expired token.
- Token for a different user, attempting cross-tenant access.
- Token with a scope that doesn't allow the call (
read:paymentstrying to write).
Expected: 401 for authentication failures, 403 for authorization failures — never 200. (New to tokens? Read What is a JWT? first.)
6. Logic abuse
- Delete something you don't own.
- Update a field documented as immutable (
id,created_at). - POST with duplicate unique fields.
- Create nested resources referencing non-existent parents.
- GET a resource after deleting it.
- Field combinations that are valid alone but break a business rule (
amount: 0.001when the minimum is0.01).
Expected: 403, 404, 409, or 422 depending on the specific violation.
7. Rate, timing and volume abuse
- Burst 1,000 requests in a second from one key.
- Send 100 concurrent identical POSTs (race condition / idempotency).
- Retry the same idempotency key with a different body.
- Cancel an operation while it is still processing.
- Send a request with a huge
Content-Lengthbut trickle the body (slowloris).
Expected: 429 with Retry-After, a clear 409 for conflicting retries, graceful degradation, no crashes.
A worked example: one field
For POST /api/v1/users with { name, email, age }:
Happy: { "name": "Alice", "email": "alice@example.com", "age": 30 } → 201.
Negative cases for just one field — email:
- Missing → 400,
code: required. - Empty string → 400.
- Whitespace → 400.
- No
@→ 400,code: invalid_format. - Double
@(a@b@c) → 400. - No TLD (
a@b) → 400 (or 200, depending on strictness — document it). - 512+ chars → 400,
code: too_long. - Unicode homograph (
аlice@example.comwith a Cyrillic а) → document the behavior. - Null byte (
alice\x00@example.com) → 400. - Already-registered email → 409,
code: EMAIL_TAKEN.
Ten cases for one field. Multiply by fields × endpoints to see why generation matters.
A worked example: one endpoint, every category
Scale the same thinking up to a whole endpoint — POST /payments with amount, currency, recipient_iban and reference — and the plan looks like this:
| Group | Example cases |
|---|---|
| Structure | each required field missing; wrong type per field; unknown extra fields; null for non-nullable fields; arrays for scalars |
| Format | amount 0, negative, 2⁵³ + 1, 0.001, "NaN", scientific notation; currency in the wrong case, unknown or 4 characters; IBAN malformed, right format but bad checksum, with spaces; reference with SQL/XSS payloads, 10 KB, newlines, RTL overrides |
| Logic | amount + currency combinations below the minimum; IBAN country not allowed; a reference that contains another customer's ID |
| Timing / race | cancel mid-processing; same idempotency key, different body; a burst of identical payments |
| Auth | expired token; token from a different tenant; token with a read-only scope |
That is easily 80–100 cases for a single endpoint. Nobody writes that by hand for every endpoint, which is why most suites stop at three negative tests per field.
What to assert (and what not to)
Always assert:
- The HTTP status is in the expected set.
- The response body matches the error envelope contract (see validation errors).
- No stack traces, internal paths, or SQL queries in the error message.
- Response time is reasonable — slow error paths are often vulnerabilities (regex DoS, for example).
Never assert:
- The exact copy of human-readable messages (they change).
- That the server used a specific implementation ("returned a MySQL error" leaks an abstraction).
- 400 when the spec says 422 — pick one per API and stick with it.
Where AI helps (and where it doesn't)
Negative testing rewards enumeration, which is exactly where people get bored and generators don't. A tester sees an email field and thinks of three bad values; a generator works through every category above for every field.
Template-based generation (empty, long, Unicode, injection strings for a string; 0, negative, overflow for a number) gets you most of the way. An AI model layered on top can add:
- Field-name awareness. A field called
ibangets IBAN-specific bad values (wrong checksum, wrong country code); apostal_codegets per-country format cases. - Cross-field inference. If
country=USimpliesstateis required, it proposescountry=US, state=nullwithout being told. - Prose-to-test. You describe a rule in plain English — "cancelling an already-executed payment returns 409 with
PAYMENT_IMMUTABLEand the status staysexecuted" — and get concrete steps: set up an executed payment, callDELETE /payments/{id}, assert 409 and the error code, re-read the payment and assert its status. The sentence stays attached to the test as its "why". - Bug-to-suite expansion. When a real bug reaches production, ask for "tests that would have caught this". You get the case that triggered it plus the nearby variants a person wouldn't think to write, so your suite slowly encodes your system's failure history.
What it still gets wrong:
- It trusts the spec over reality. If the spec says
format: date-timebut the service stores a timezone-naive string, generated tests fail until the spec is fixed — arguably correct, but noisy. - Over-confident assertions. A generated
assert status == 500usually means the model mistook a bug for expected behavior. - Missing domain idioms. Settlement cut-offs, holiday calendars and "net 30" terms aren't in the spec, so nothing invents them.
- Redundant cases. Several generated cases often hit the same code path. Review and de-duplicate.
Guardrails that apply to any generated negative suite:
- Run it against a safe environment, never production — not even "read-only" cases; a generator can produce a DELETE it thought was harmless.
- Rate-limit the runner so a burst of fuzz cases doesn't take down your own staging.
- Keep a short business-rules file the generator must respect (for example, a maximum transfer amount in staging).
- Read the generated tests before you commit them. Once merged, you own them.
- Re-review after every spec change instead of auto-merging regenerated cases.
In Shift-Left API, a deterministic engine plans negative, boundary and auth-bypass scenarios from your API definition without any AI key; if you connect a model, it refines those tests and adds a few extra per endpoint.
The security overlap
Negative testing overlaps heavily with application security testing:
- Injection tests ≈ SQL/command/XSS injection.
- Auth abuse tests ≈ authorization pen-testing.
- Rate abuse tests ≈ DoS resilience.
Treat a negative suite as a first line of defense, not a security programme. It won't tell you whether one user can read another user's objects across a whole API, which is what dedicated API security testing covers (the OWASP API Security Top 10 is the usual checklist).
Common mistakes
1. Treating all 4xx as "good enough". A 401 and a 403 mean different things. Assert on the specific code.
2. Only testing one bad field at a time. Combined bad inputs trigger paths single-field tests miss.
3. Assuming the server will 400. Plenty of servers return 200 with { "success": false } — or worse, 500. Test, don't assume.
4. Stopping at the happy-adjacent. "abc" instead of 123 is barely negative. Real negative tests use adversarial inputs: nulls, huge payloads, Unicode abuse.
5. Not running negative tests in CI. They catch regressions that happy paths can't. Make them part of every build.
6. Running generated cases once and forgetting them. Generation is a loop: new field, new cases, new review.
Sources and further reading
- RFC 9110 — HTTP Semantics, status codes
- RFC 9457 — Problem Details for HTTP APIs
- OWASP API Security Top 10
- OWASP Web Security Testing Guide — Input Validation Testing
What's next
Negative testing catches broken behavior on bad input. Next in the course: retries and timeouts — what happens when the network misbehaves. When you're ready to go deeper, the API testing hub has the full guides.
Frequently asked questions
What is negative testing in API testing?+
It is testing an API with invalid, unexpected or hostile input — missing fields, wrong types, oversized payloads, bad tokens — and checking that it rejects the request with the right status code and a safe error, instead of crashing or succeeding.
What is the difference between positive and negative test cases?+
A positive case sends valid input and expects success (200, 201). A negative case sends invalid input and expects a controlled failure (400, 401, 403, 404, 409, 422 or 429). A good suite has several negative cases for every positive one.
What status code should a negative test expect?+
It depends on the failure: 400 for malformed or invalid input, 401 for missing or bad credentials, 403 for a valid user without permission, 404 for a missing resource, 409 for a conflict, 422 if your API uses it for validation, 429 for rate limits. Never 500.
How many negative test cases do I need per endpoint?+
There is no fixed number. Cover each of the seven categories that applies to the endpoint, and for each input field test missing, wrong type, boundaries and format. That is usually dozens of cases per endpoint, which is why teams generate them.
Can AI write negative test cases?+
Yes. Generators are good at enumerating bad inputs and at turning a plain-English rule into steps. You still need to review assertions, remove duplicates, and run the suite only against non-production environments.
How do you do negative testing for an API?+
Send deliberately wrong, malformed, malicious, or adversarial inputs and assert that the server responds safely with a clean error rather than a crash, a leak, or an unintended success. Assert the specific status code and error envelope, and run the suite in CI against a non-production environment.
What are common negative scenarios for API testing?+
The main categories are malformed syntax, type abuse, boundary abuse, encoding and injection payloads, auth abuse such as expired or cross-tenant tokens, logic abuse such as deleting something you do not own, and rate, timing, and volume abuse such as bursts and races.
What is an example of negative test cases for one field?+
For an email field on user creation, send it missing, empty, whitespace-only, without an @ sign, with two @ signs, over 512 characters, and with a null byte, expecting 400 each time. An already-registered email should return 409.
Related lessons
Most API bugs live in input validation. Here's how to test it systematically.
The network is unreliable. Here's how clients should retry, how servers should behave, and how to test both.
2xx means success, 4xx means you messed up, 5xx means they messed up — but the details matter. Here's the list every tester should know by heart.
Read more on the blog
What API testing is, in simple words: the types of API tests, what to verify on every endpoint, how API testing works step by step, the tools for each job, and the practices that keep a suite fast and trustworthy.
Learn how to generate test data for API testing with practical techniques. Covers spec-driven generation, boundary value analysis, combinatorial strategies, and automated tooling.