Negative API Testing

Why negative API checks matter and which classes of invalid requests QA engineers should test regularly.

Updated:

Contents

Positive checks show that an API handles expected scenarios. Negative checks reveal its resilience to invalid, unexpected, and dangerous inputs. This is particularly valuable for QA because it often exposes weaknesses in validation, error handling, security, and service stability.

Users and integrations rarely behave perfectly. They may send empty fields, incorrect types, expired tokens, oversized payloads, or invoke endpoints in invalid states. A good API must respond predictably and safely, beyond simply avoiding a crash.

What negative API testing means

Negative API testing examines system behavior with invalid, unexpected, forbidden, or conflicting input.

It covers much more than sending an empty field:

  • →Missing required fields
  • →Incorrectly formatted values
  • →Mismatched data types
  • →Out-of-range values
  • →Field combinations that violate business rules
  • →Unauthenticated or unauthorized users
  • →Oversized payloads
  • →Unexpected Content-Type
  • →Methods that should not be supported at that endpoint
  • →Repeated requests that create conflicts

Negative testing therefore checks both input correctness and API resilience to misuse.

Why this matters especially for APIs

A UI offers some protection through forms, masks, dropdowns, and disabled buttons. APIs do not have that same layer. They receive raw requests and must decide how to handle them.

Weak negative API testing typically leaves three risks:

  • →The service fails on bad input.
  • →The service behaves unpredictably.
  • →The service accepts something it should reject.

If you test only the happy path, you see a very limited part of real system behavior.

What to challenge

Good negative testing follows input classes rather than arbitrary attempts to enter nonsense.

Regularly test these groups:

  • →A required field is absent.
  • →A field is present but empty.
  • →A field is present but null.
  • →The type is wrong: a string instead of a number, or an object instead of an array.
  • →A string exceeds the maximum length.
  • →A number is below the minimum or above the maximum.
  • →A date, email, phone, UUID, or enum value has an invalid format.
  • →Parameters form an invalid combination.
  • →Unexpected extra fields are included.
  • →The request method is incorrect.
  • →Content-Type is incorrect.
  • →The token is corrupted or expired.
  • →The request targets another user's resource.
  • →The request body is too large.
  • →The request is repeated in a conflicting state.

A systematic approach turns these checks into a repeatable API quality model rather than chaotic attempts to break something.

What makes a good negative test

A weak negative test checks only that an error occurred. A strong one examines much more.

  • →Is the status code correct?
  • →Is the error understandable to the client?
  • →Does the response expose unnecessary internal details?
  • →Did any data change when it should not have?
  • →Were there side effects?
  • →Does repeating the same invalid request produce consistent behavior?
  • →Does the service avoid returning 500 for a client-side problem?

For example, invalid JSON generally warrants 400 rather than 500. An attempt to modify another user's resource usually calls for 403 or 404, rather than success. An oversized body may warrant 413. The goal is consistent, understandable handling rather than guessing one ideal code for every case.

What to inspect beyond the error

A strong habit is always checking system state after an unsuccessful request.

  • →Was no record actually created?
  • →Did the object's status remain unchanged?
  • →Was no money charged?
  • →Was no email sent?
  • →Did auditing and logging record the event correctly?
  • →Did repeating the request leave the system in a valid state?

Many dangerous defects hide here. The API returns an error externally but has already partially changed data internally. Users may see the consequences later as an apparently random bug, although the root cause was poor negative-scenario handling.

Practical example

Consider a user creation endpoint.

The positive scenario is straightforward: send a valid email, name, and password and receive 201 Created.

Now consider negative scenarios:

  • →Email is missing.
  • →Email is empty.
  • →Email is invalid.
  • →The password is too short.
  • →A body field has an unexpected type.
  • →Authorization is absent when required.
  • →The same email is registered again.
  • →The body is too large.
  • →Content-Type is not application/json.

QA should verify that:

  • →The server responds predictably.
  • →The error is sufficiently clear to the client.
  • →The status code matches the problem class.
  • →No invalid record is created.
  • →No unintended side effects occur.
  • →Repeating the same invalid request does not further damage the system.

At this point, negative testing becomes a substantive assessment of API maturity rather than an optional extra.

Key takeaways for QA engineers

  • →Negative API testing examines resilience alongside input errors.
  • →A good negative test checks both the error code and subsequent system state.
  • →A strong API handles bad requests safely and predictably.
  • →The happy path alone almost never demonstrates real service reliability.
  • →Build negative checks around risk classes rather than random attempts to break something.

Further reading

  • →In this knowledge base: Methods, Status Codes, and Headers
  • →In this knowledge base: Authentication and Authorization
  • →External resource: OWASP REST Security Cheat Sheet

A local negative-test matrix with side-effect checks

Use a synthetic local contract rather than guessing what every API should return. The prepared practice fixture accepts POST /items with an administrator training token, Content-Type application/json, a name of 3–12 ASCII letters and an integer quantity from 1 to 5. The body must contain exactly those two fields. Data stays in memory; start a fresh server for each independent test.

plain text
Case	Example	Expected in this fixture
Missing field	{"name":"abc"}	422 invalid_fields
Wrong type	{"name":"abc","quantity":"1"}	422 invalid_quantity
Out of range	{"name":"abc","quantity":6}	422 invalid_quantity
Extra field	{"name":"abc","quantity":1,"admin":true}	422 invalid_fields
Malformed JSON	{	400 malformed_json
Wrong media type	Content-Type: text/plain	415 json_required
Body over 1024 bytes	1025-byte body	413 payload_too_large

Before each rejected write, GET /items and record total; repeat afterward and require an unchanged total. This catches an implementation that returns an error after writing a record. For an accepted new name expect 201 and total + 1; submitting it again returns 409 with no second addition. These statuses describe this declared contract, not a rule that all products must use.

plain text
python3 mock_api.py --port 8765
# In another terminal, using only the synthetic local token:
curl -i http://127.0.0.1:8765/items -H "Authorization: Bearer demo-admin" -H "Content-Type: application/json" --data '{"name":"abc","quantity":6}'
python3 -m unittest test_mock_api.py -v

The test fixture is intentionally local and simplified. Use it to practice input selection and evidence collection; do not copy its fixed tokens or HTTP server into a production authentication system.

Continue learning

Download the local practice lab (ZIP)

Check authorization separately

Derive boundary values