API Testing
Test application programming interfaces directly — sending requests, validating status codes, headers, and response bodies — without going through the UI. This is where most of the interesting business logic actually lives.
What it is
API testing means interacting directly with an application’s backend endpoints — sending HTTP requests and verifying the responses — rather than driving the application through a browser or mobile UI. Because most modern applications are built on top of APIs, testing them directly gives you faster feedback, earlier in the cycle, and with finer-grained assertions than UI testing allows.
An API test asks: given this request (URL, method, headers, body), does the system return the right response (status code, body schema, data values, error messages)? It’s specification-based testing applied to a service contract rather than a user interface.
Why test at the API level? UI tests are slow, brittle, and expensive to maintain. API tests run in milliseconds, don’t break when a button moves, and can be run thousands of times a day in CI. If the API is correct, the UI just needs to call it correctly — a much smaller testing problem.
When to use it
- When the UI is not yet built — test the backend immediately after backend development, without waiting for frontend work
- When you need to test business logic independent of the frontend — price calculations, eligibility rules, data transformations
- When validating integrations between services — does Service A send the right data to Service B?
- For regression testing after backend changes — fast, repeatable, and doesn’t require a browser
- For performance and load testing — drive hundreds of concurrent requests directly at the API
Key concepts
HTTP methods
Each HTTP method has a defined semantic meaning. Verify that your API uses them correctly:
| Method | Purpose | Idempotent? | Example |
|---|---|---|---|
| GET | Retrieve a resource | Yes | GET /orders/123 |
| POST | Create a new resource | No | POST /orders |
| PUT | Replace a resource entirely | Yes | PUT /orders/123 |
| PATCH | Update part of a resource | No | PATCH /orders/123 |
| DELETE | Remove a resource | Yes | DELETE /orders/123 |
Status codes to know
Status codes are the first thing you check. A wrong status code is a bug, even if the response body looks right.
| Code | Meaning | When to expect it |
|---|---|---|
| 200 OK | Success | Successful GET, PUT, PATCH |
| 201 Created | Resource created | Successful POST that creates a new record |
| 400 Bad Request | Client error — malformed input | Missing required field, wrong data type |
| 401 Unauthorised | Not authenticated | No auth token or invalid token |
| 403 Forbidden | Authenticated but not allowed | User doesn’t have permission for this resource |
| 404 Not Found | Resource doesn’t exist | GET /orders/99999 where that order doesn’t exist |
| 422 Unprocessable Entity | Validation failed | Input is syntactically valid but semantically wrong (e.g. invalid NZ postcode) |
| 500 Internal Server Error | Server crashed | Unhandled exception — always a bug |
Anatomy of a request
Every API request has four possible components. Understanding what goes where prevents common test setup errors:
- URL — the endpoint address, including path parameters:
https://api.example.co.nz/v1/orders/123 - Headers — metadata sent with every request. Key ones to test:
Authorization: Bearer <token>(authentication) andContent-Type: application/json(tells the server how to parse the body) - Query parameters — filtering and sorting options appended to the URL:
?status=pending&limit=10 - Request body — the data sent with POST/PUT/PATCH requests, usually as JSON
What to validate in the response
A thorough API test checks more than just the status code:
- Status code — does it match what the spec says?
- Response body schema — are all expected fields present? No extra unexpected fields?
- Data types — is
pricea number (not a string)? IsorderIda string UUID? - Required fields — if the spec says
customerIdis always returned, verify it’s never null or missing - Error messages — on 4xx responses, is the error message clear, actionable, and free of internal implementation details?
- Response time — does the endpoint respond within an acceptable SLA?
Contract testing
If your API has an OpenAPI (Swagger) specification, treat it as your test oracle. The spec defines the contract: what requests the API accepts, what responses it returns, and what each field means. If the live API doesn’t match the spec, that’s a bug — even if the API appears to “work.” Consumers of the API (frontend, mobile app, partner integrations) are coding to the spec, not to whatever the API happens to return today.
NZ worked example
Imagine you’re testing an e-commerce API for a New Zealand retailer. Here’s a sequence of API tests covering the order creation flow:
| Test | Request | Expected status | Key assertions |
|---|---|---|---|
| Create order with valid NZ address | POST /orders body: valid NZ shipping address (e.g. 123 Queen St, Auckland 1010) |
201 Created | Response contains orderId, status: "pending", total in NZD |
| Retrieve the created order | GET /orders/{orderId} | 200 OK | Returned order matches what was posted; orderId matches |
| Create order with invalid NZ postcode | POST /orders body: postcode "9999" (not a valid NZ postcode) |
422 Unprocessable Entity | Error message mentions postcode; no order created in the database |
| Create order without auth token | POST /orders No Authorization header |
401 Unauthorised | No order created; error body does not expose internals |
| Retrieve order belonging to another customer | GET /orders/{otherCustomerOrderId} | 403 Forbidden | System does not return another customer’s order data (IDOR check) |
| Get order that does not exist | GET /orders/00000000-0000-0000-0000-000000000000 | 404 Not Found | Clear error message; no stack trace in body |
Note that the NZ-specific context matters: NZ postcodes are 4-digit numbers (1010 for central Auckland, 6011 for central Wellington, 8011 for central Christchurch). A postcode of “9999” or “12345” is invalid and should be caught at the API layer, not just in the UI.
Common bugs API testing finds
- 200 when it should be 400 — the API accepts clearly invalid input without complaint, then fails silently later
- Stack traces in error responses — a 500 response that includes a Java stack trace or file path is a security risk, not just an aesthetic issue
- Missing required fields — the spec says
customerIdis always returned, but it’s absent for guest orders - Wrong data types —
pricereturned as a string ("49.99") instead of a number (49.99), breaking downstream calculations - No authentication required on protected endpoints — removing the
Authorizationheader and still getting a 200 is a critical security bug - CORS headers missing or too permissive —
Access-Control-Allow-Origin: *on an authenticated endpoint allows cross-site requests from any domain - Inconsistent IDs — POST returns
id: 123but GET expects/orders/00000123(zero-padded) — integration breaks
Tools
- Postman — the most widely used API testing tool; supports collections, environments, and automated test scripts written in JavaScript. Good for manual and automated API testing.
- Insomnia — lighter-weight alternative to Postman; good for individual API exploration
- REST-assured — Java library for writing API tests as code; integrates with JUnit/TestNG; preferred in Java-heavy teams
- k6 — designed for performance and load testing APIs; scripts in JavaScript; can run from CI
- Newman — Postman’s CLI runner; run your Postman collections from the terminal or CI pipeline without opening the GUI
ISTQB mapping
| Syllabus ref | Topic | Level |
|---|---|---|
| CTFL 4.0 — 2.2 | Component integration testing — testing interfaces between components | Foundation |
| CTAL-TA 3.3 | Structural testing at integration level; API contract verification | Advanced / Senior |
| CTAL-TA 4.2 | Test specification for service interfaces and integration points | Advanced / Senior |
Tips
Read the spec before you test. Start by reading the API documentation (OpenAPI/Swagger) before testing. Treat the spec as your test oracle — if the API doesn’t match the spec, that’s a bug even if the API “works.” Teams often discover that the spec and implementation have quietly diverged, causing silent failures in client applications.
- Test auth first — before testing happy paths, verify that removing or corrupting the auth token returns 401. If it doesn’t, stop and raise it immediately.
- Use environments in Postman — store your base URL, tokens, and IDs in environment variables so your test collection runs against dev, staging, and production with a single click.
- Chain requests — use the ID returned from a POST as input to the subsequent GET, PUT, and DELETE tests. This is more realistic than hardcoded IDs and finds sequencing bugs.
- Test the boundaries of optional fields — what happens when an optional field is null? Omitted entirely? An empty string? These three are often handled differently.
- Check idempotency — send the same PUT request twice. The second call should return the same result and not create a duplicate record.
Practice this technique: Try Test Lead Practice 06 — API contract bugs.
NZ example — testing a NZ payments API
A NZ-specific payments API (such as one built on the Payments NZ API Centre standards) has several NZ-specific test cases beyond the generic HTTP contract tests.
- Bank account format validation — NZ bank accounts follow the BB-bbbb-AAAAAAA-SS format (bank-branch-account-suffix). The API should accept
06-0000-0000000-00(ASB) but reject1234567890123456(card number format). Test the API directly with both formats and verify the response codes and error messages. - Currency — the API should only accept NZD amounts. Test with
"USD"in the currency field — expect a 400 or 422 with a clear error. - Amount precision — NZD amounts must have exactly 2 decimal places. Test
100,100.0,100.00,100.000— only100.00and100.0(if normalised) should succeed;100.000should be rejected or normalised. - POLi integration — POLi is a NZ/AU bank-to-bank payment method. If the API supports POLi, test the redirect flow, the callback handling on success/failure/timeout, and what happens if the bank session expires mid-flow.
- IBAN — NZ does not use IBAN. Any API that requires or accepts IBAN for NZ accounts has a design defect — test for this.