20 min read · 9 self-checks · Updated June 2026

Structural / Integration · CTFL 4.0, CTAL-TA

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.

Senior Test Lead ISTQB CTFL 4.0 · CTAL-TA

1 The Hook

A KiwiSaver provider ships a new mobile app. The team tests it the way most teams test: open the app, log in, tap through the screens, check the balance shows. Everything looks fine in the browser-driven walkthrough, so it goes live.

Two weeks later, support is flooded. Some members are seeing another member’s balance. The bug was never in the UI — it was in the backend endpoint GET /members/{id}/balance, which returned a balance for any id the caller asked for, with no check that the logged-in member actually owned that account. The UI only ever requested the member’s own id, so clicking around the app could never surface it. A single direct request to the endpoint with someone else’s id would have caught it in minutes.

This is the pattern: the business logic, the authorisation rules, the data validation all live in the API, not the screen. Test only through the UI and you are testing a thin layer over the part that actually matters — and the most damaging defects sit underneath it, where a browser click never reaches.

💡
Key Takeaway

API testing means sending HTTP requests directly to backend endpoints and asserting the status code, response body, and authorisation behaviour — bypassing the UI entirely to reach the business logic that actually matters. Use it whenever you need to verify security rules, data validation, or service integrations, because the UI only ever sends the requests its own screens generate and will never expose an IDOR or a missing auth check. The mistake most testers make is asserting only the status code: a 200 response can still return the wrong data type, a missing required field, or another customer’s data — the status code is the first assertion, never the only one.

💬
Senior Engineer Insight

The most dangerous API test suite I have ever audited had 340 tests and zero failures. It also had zero assertions. Every request had a status-code check and nothing else — so when a government API quietly started returning another user's data inside a 200 response, the suite went green. The team thought they had coverage. They had expensive HTTP pinging. Real API testing means asserting the response body shape, the data types, the required fields, and — above everything else — that your auth token actually gates access. I now have a rule: the first test against any new endpoint is to remove the Authorization header. If you still get a 200, nothing else matters. Fix that before you write another line.

2 The Rule

Test the API directly — send the request and assert the status code, the response body, and the authorisation behaviour — because the logic that matters lives in the service, not the screen that calls it.

3 The Analogy

Analogy

Checking a building by talking to the front desk versus walking the wiring.

UI testing is asking the receptionist at a HealthNZ hospital, “Is the building safe?” They smile and say yes — the lobby looks tidy, the lights are on. But the receptionist only sees the front desk. The fire wiring, the locked drug cupboards, the staff-only doors that should never open for a visitor — none of that is visible from the lobby.

API testing is the building inspector who walks the service corridors, opens the panels, and tries each locked door to see whether it actually stops the wrong person. The lobby (the UI) can look perfect while a staff-only door (a protected endpoint) swings open for anyone. You only find that by going behind the front desk and testing the wiring directly.

Common Mistake vs What Works

✗ Common mistake

Testing only the happy path: a valid POST returns 201, so the test passes. Status codes are verified but the response body is never asserted — no check that the required fields are present, that amount is a number and not a string, or that the currency is NZD. Error paths (400, 401, 403, 404, 429) are left to the developers to discover in production.

✓ What actually works

Test the full status-code map for each endpoint: valid inputs (201/200), invalid inputs (400 with a matching error schema), missing or expired auth (401), wrong-user access such as an IDOR attempt (403), non-existent resource (404), and rate-limit breach (429). Assert the complete response shape on every call — required fields, data types, and NZ-specific formats (NZD currency, 4-digit postcodes, BB-bbbb-AAAAAAA-SS bank accounts). A 200 with an empty body or a wrong data type is still a bug; the status code is the first assertion, never the only one.

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:

HTTP methods and their purpose
MethodPurposeIdempotent?Example
GETRetrieve a resourceYesGET /orders/123
POSTCreate a new resourceNoPOST /orders
PUTReplace a resource entirelyYesPUT /orders/123
PATCHUpdate part of a resourceNoPATCH /orders/123
DELETERemove a resourceYesDELETE /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.

Status codes — what they mean and when they should appear
CodeMeaningWhen to expect it
200 OKSuccessSuccessful GET, PUT, PATCH
201 CreatedResource createdSuccessful POST that creates a new record
400 Bad RequestClient error — malformed inputMissing required field, wrong data type
401 UnauthorisedNot authenticatedNo auth token or invalid token
403 ForbiddenAuthenticated but not allowedUser doesn’t have permission for this resource
404 Not FoundResource doesn’t existGET /orders/99999 where that order doesn’t exist
422 Unprocessable EntityValidation failedInput is syntactically valid but semantically wrong (e.g. invalid NZ postcode)
500 Internal Server ErrorServer crashedUnhandled 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) and Content-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 price a number (not a string)? Is orderId a string UUID?
  • Required fields — if the spec says customerId is 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:

NZ e-commerce order API — test scenarios
TestRequestExpected statusKey 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 customerId is always returned, but it’s absent for guest orders
  • Wrong data typesprice returned as a string ("49.99") instead of a number (49.99), breaking downstream calculations
  • No authentication required on protected endpoints — removing the Authorization header and still getting a 200 is a critical security bug
  • CORS headers missing or too permissiveAccess-Control-Allow-Origin: * on an authenticated endpoint allows cross-site requests from any domain
  • Inconsistent IDs — POST returns id: 123 but 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

ISTQB reference
Syllabus refTopicLevel
CTFL 4.0 — 2.2Component integration testing — testing interfaces between componentsFoundation
CTAL-TA 3.3Structural testing at integration level; API contract verificationAdvanced / Senior
CTAL-TA 4.2Test specification for service interfaces and integration pointsAdvanced / 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 (Southern Bank) but reject 1234567890123456 (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 — only 100.00 and 100.0 (if normalised) should succeed; 100.000 should 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.

4 Industry Reality

🏭 What you actually encounter on the job
  • The spec is always out of date. OpenAPI documents are written at design time and rarely updated as the API evolves. Senior testers read the spec as a starting point, then compare it against what the API actually does and raise discrepancies as defects. Treating a stale spec as gospel is how you miss the real behaviour.
  • Auth tokens expire mid-test-run. That collection you built in Postman worked fine yesterday. Today every request returns 401 because the OAuth token expired at midnight. Real test suites include a pre-request script that refreshes the token automatically; juniors manually paste a new token every morning.
  • Environments are never quite right. Dev has no real payment gateway. Staging has stale data that doesn’t match production schema. Production is off-limits (usually). You learn to test with mocks and stubs for external services, and to maintain your own test data rather than depending on what the environment happens to contain.
  • Shared test data causes flaky tests. Five testers running API tests against the same dev environment, all using the same order IDs, means tests fail randomly because someone else deleted or modified the record. The fix is test isolation: each test creates its own data and cleans it up. In NZ government and finance projects this is especially fraught because test environments often contain production-like data that can’t be freely created or deleted.
  • Most teams skip negative tests under time pressure. Happy-path API tests get written. The auth-missing, IDOR, and invalid-input scenarios get deferred to “later” and never written. Senior testers push back on this specifically for authorisation tests, because those are the ones that turn into breach headlines. If you only have time for one negative test per endpoint, make it the one where you remove the auth token.

Senior engineer insight

I spent six months on a HealthNZ integration project where every sprint ended the same way: the UI looked perfect, the team was happy, and the API was quietly broken. We had an endpoint that returned a patient’s appointment history — GET /patients/{nhi}/appointments — and nobody had ever sent a request with a different NHI than the logged-in user. One afternoon I swapped the NHI in Postman and got back someone else’s appointments in a 200 response. That was the moment I stopped treating API testing as an optional extra and started treating auth-and-IDOR as the mandatory first tests for every endpoint, before I even look at the happy path. The most serious bugs in NZ health and government APIs are never in the fields the UI fills in — they’re in the ID parameters the UI never varies.

The most common mistake I see from graduates is writing ten happy-path tests and zero negative tests — they prove the API works when everything goes right, which is the only scenario an attacker will never use.

From the field

On a central government project integrating with the AoG API standards gateway, the team assumed every endpoint was protected because the RealMe SAML assertion was validated at the gateway layer. What nobody had checked was whether the downstream services enforced their own authorisation — they just trusted whatever the gateway forwarded. During exploratory testing I replayed a legitimate JWT with a different user's Revenue NZ number in the sub claim and got back that user's tax position in a 200 response. The gateway had authenticated the token correctly; the downstream Revenue NZ integration API had simply never been built to check whether the subject in the token matched the resource being requested. The lesson: gateway auth and endpoint auth are two separate things. In NZ government API architectures where AoG-standardised gateways sit in front of legacy core systems, you must test authorisation at every layer, not just at the edge.

5 When to Use It — and When Not To

⚡ Decision guide

✓ Use it when

  • The UI is not built yet — API testing lets you verify business logic independently of frontend progress, and find bugs before they compound through layers
  • You need to test authorisation at scale — IDOR, cross-tenant access, and privilege escalation are only reliably caught by sending direct requests, not by clicking UI screens
  • You’re testing a service integration — does Service A send the payload that Service B expects? Only a direct API test answers this without noise from the UI
  • Regression testing after backend changes — a Postman collection or REST-assured suite runs in seconds and gives you instant confidence that existing contracts haven’t broken
  • Performance or load testing is required — driving load through a browser UI introduces artificial bottlenecks; hitting the API directly gives you true throughput numbers

✗ Skip it when

  • The entire application is a third-party black box with no API access and no spec — you have nothing to test against; use UI-driven exploratory instead
  • You’re testing UI-only concerns — layout, accessibility, visual rendering, and navigation flow are not API questions; don’t write an API test to check that a button looks right
  • The team has no spec and no contract at all — testing without a defined contract means you’re just observing current behaviour; spend the time getting a spec written first
  • You’re already covered by contract tests — if the team runs Pact or a similar contract-testing tool across every consumer/provider pair, duplicating that in a separate API collection wastes maintenance budget
  • The endpoint is deprecated and already removed from all call sites — testing dead code is low-value; document the removal and move on

Context guide

How the right level of API Testing effort changes based on project context.

Context Priority Why
Regulated government or finance system (NZISM, AoG API standards, Privacy Act 2020) Essential IDOR and missing-auth bugs in Revenue NZ, Benefits NZ, CoverNZ, or HealthNZ APIs carry Privacy Act 2020 consequences; direct endpoint testing is the only reliable way to catch them before a Privacy Commissioner investigation does.
Microservices or third-party integrations (Payments NZ, RealMe, open banking) Essential Service boundaries are the riskiest part of a distributed system; API tests are the only way to confirm that payload formats, field names, and error-handling contracts match what each side expects.
Mobile backend or SPA with a dedicated API layer High The mobile app and web frontend share the same API; a contract break that passes UI automation on one client silently breaks the other — only direct API tests catch schema drift before it reaches both release channels.
Agile sprint team with CI/CD pipeline High API tests run in milliseconds and give per-PR feedback without a browser; they are the highest-value test layer to wire into CI early in a sprint cycle, well before an E2E suite is stable enough to run reliably.
Legacy migration or parallel-run (replacing a monolith with microservices) Medium The old and new endpoints must return identical responses; API tests are the right tool for parity checks, but the main risk is that the legacy system has no spec — invest time writing a contract before writing tests against it.
Small startup or MVP with a single-developer backend and no OpenAPI spec Low Without a defined contract, automated API tests only record today’s behaviour; the highest-value investment at this stage is exploratory API testing to document what the endpoints actually do, then get that signed off as the spec before building a suite.

Trade-offs

What you gain and what you give up when you choose API Testing.

Advantage Disadvantage Use instead when…
Fast execution — API tests run in milliseconds, not seconds. A suite of 200 tests finishes in under a minute, enabling genuine CI on every PR. Setup overhead is real. You need test data, tokens, environment variables, and a running service before a single test can execute. Cold-start cost is higher than a unit test. You need to verify business logic in complete isolation from the database or network — use unit tests instead.
Catches authorisation bugs UI tests will never find — IDOR, missing auth, privilege escalation. These are the defects with the largest real-world blast radius in NZ government and finance. Cannot verify the user experience. A correctly implemented API can still be wired to a broken UI — API tests will not catch a button that submits to the wrong endpoint or a field that silently drops the user’s input. The defect you are hunting lives in the presentation layer (layout, navigation, form wiring) — use a browser-driven test instead.
Stable and low-maintenance. API contracts change far less frequently than UI layouts. A test collection written against a stable OpenAPI spec can run unmodified for months. Without a defined contract (OpenAPI spec or agreed schema), you have no test oracle. You end up recording current behaviour rather than verifying correctness — creating noise, not coverage. No spec exists yet — invest that time in defining the contract first; API tests written without an oracle are expensive to maintain and prove nothing.
Verifies service integrations cleanly. When a TransitNZ licensing system calls the TransitNZ vehicle API, only a direct API test can confirm the payload format, field names, and error handling are correct — an E2E UI test buries that signal under too many layers. Test data isolation is hard. Shared environments mean tests collide — one tester’s DELETE removes the record another tester’s GET is about to read. Getting true isolation requires each test to create and clean up its own data, which adds complexity. Both teams have agreed to consumer-driven contract tests (Pact) — adding a parallel API test suite duplicates maintenance cost without adding coverage.
Enables shift-left security. Running IDOR and auth-missing checks on every PR means security regressions are caught by the author, not by a penetration tester six weeks later at the cost of a release delay. OAuth token management is a constant friction point. Tokens expire, scopes change, and test environments often share a single service account whose token needs manual refreshing — a significant source of false-failure noise in CI. The system has no API surface at all (a third-party black-box SaaS with no integration points) — exploratory testing through the UI is the only available option.

6 Best Practices

✓ What experienced testers do
  • ✓ Test auth before the happy path. The very first test against any endpoint is: remove the Authorization header and confirm you get 401. If you get 200, you have a critical bug and there is no point continuing until it is fixed.
  • ✓ Write one IDOR test per resource type. For any endpoint that takes an ID in the path (GET /accounts/{id}, GET /orders/{id}), test that a user authenticated as Customer A cannot retrieve Customer B’s record. This is the most common and highest-severity class of API bug in NZ fintech and government systems.
  • ✓ Chain requests using captured values, not hardcoded IDs. The POST creates a record and returns an id; capture it and feed it to the GET, PUT, and DELETE. Hardcoded ids hide format mismatches and sequencing bugs.
  • ✓ Assert the full response shape, not just the status code. A 201 that returns an empty body or the wrong schema is still a defect. Check that all required fields are present, that optional fields are absent when they should be, and that data types match the spec.
  • ✓ Check that error responses leak nothing internal. Every 4xx and 5xx response body should be reviewed manually at least once. Stack traces, file paths, SQL statements, and framework version strings in error responses are security defects, not style issues.
  • ✓ Use environments, not hardcoded base URLs. A Postman collection (or k6 script) with the base URL hardcoded to http://dev.internal can never run against staging or production. Store base URL, tokens, and test IDs in environment variables from day one.
  • ✓ Test idempotency for PUT and DELETE. Send the same PUT twice with identical bodies: the second response should match the first and no duplicate should be created. Send DELETE twice: the second should return 404 (or 204 again if designed that way) — confirm with the spec.
  • ✓ Validate NZ-specific data formats at the API layer. NZ bank accounts (BB-bbbb-AAAAAAA-SS), NZ Revenue NZ numbers (check-digit validated), NZ postcodes (4-digit, not 5-digit US format) — all of these should be rejected by the API, not just the UI. Test them directly.
  • ✓ Add response-time assertions to every test. Even in functional test suites, asserting that the response arrived in under 2 seconds gives early warning of performance regressions without needing a separate load-testing pass.
  • ✓ Version your Postman collections alongside the code. Export the collection JSON and commit it to the repository so test changes are reviewed in the same PR as API changes. Postman collections that live only in one person’s account are a single point of failure.

7 Common Misconceptions

❌ Myth: “If the UI works, the API is fine — they’re testing the same thing.”

Reality: The UI tests one specific call sequence generated by the frontend code. The API accepts any caller — other services, mobile apps, partner integrations, and attackers. The UI never removes its own auth token. It never requests another user’s record. It never sends a malformed payload. UI tests and API tests are complementary, not interchangeable; the most dangerous defects live precisely in the gap between them.

❌ Myth: “A 200 status code means the test passed.”

Reality: A status code only tells you the server processed the request without crashing. A 200 response can contain an empty body, wrong data types, a missing required field, or someone else’s data. A test that asserts only the status code passes while the API silently corrupts business logic. Status code is the first assertion, never the only one. Senior testers assert schema, data types, required fields, error message content, and response time as a matter of course.

❌ Myth: “API testing is only for developers — testers work at the UI level.”

Reality: In modern NZ teams, testers are expected to work across the stack. The ISTQB CTAL-TA syllabus explicitly covers API and integration testing, and job descriptions for senior QA and test lead roles in NZ consistently list Postman and REST-assured as required skills. Restricting yourself to UI testing means you will never find the IDOR and authorisation bugs that cause the biggest incidents — and you will spend far more time maintaining slow, brittle browser tests than an equivalent API suite requires.

8 Now You Try

Three graded exercises — spot, fix, then build. Write your answer, run it for AI feedback, then compare to the model answer.

🔍 Exercise 1 of 3 — Spot: pick the right status code

An Revenue NZ myIR API exposes GET /returns/{id} and POST /returns. For each request below, state the expected HTTP status code and one sentence on why. (a) POST a new valid return; (b) GET a return id that does not exist; (c) GET another taxpayer’s return id while logged in as yourself; (d) POST a return with no auth token; (e) POST a return whose Revenue NZ number fails the check-digit rule.

Show model answer
(a) POST valid return — 201 Created. A POST that creates a new resource returns 201, and the body should include the new return id.
(b) GET non-existent id — 404 Not Found. The resource does not exist; the body should be a clear message with no stack trace.
(c) GET another taxpayer's return — 403 Forbidden. You are authenticated but not allowed to see someone else's data. Returning 200 here would be a critical IDOR bug. (404 is also defensible to avoid confirming the id exists, but it must not be 200.)
(d) POST with no auth token — 401 Unauthorised. No identity was supplied, so the request is rejected before any authorisation check.
(e) POST with bad Revenue NZ check digit — 422 Unprocessable Entity (or 400). The request is well-formed JSON but semantically invalid; the error message should name the Revenue NZ-number field.

The two that catch people out: 401 vs 403 (not authenticated vs not allowed) and 400 vs 422 (malformed vs semantically invalid).
🔧 Exercise 2 of 3 — Fix: repair a shallow API test

A tester wrote the test below for an Harbour Bank payments endpoint. It only checks the status code, so it would pass even while the API is badly broken. Rewrite it to add the assertions a senior would expect (response schema, data types, currency, authorisation, no internals leaked).

Shallow test:
POST /payments with a valid NZD payment body.
Assert: status code is 200.
(That is the only assertion.)

Rewrite with the missing assertions:

Show model answer
A POST that creates a payment should return 201, not 200 — so the original expected code is likely wrong too.

Response body should contain: a paymentId, a status (e.g. "pending"/"settled"), the amount, and the currency.
Data types to assert: amount is a number (not the string "49.99"); paymentId is a string; status is one of the allowed enum values; no null required fields.
Currency / amount assertions: currency is "NZD"; amount has exactly 2 decimal places; a USD amount is rejected with 400/422.
Authorisation tests to add: same request with no Authorization header returns 401; a request for another customer's account returns 403 (IDOR check).
Security checks: a 4xx/5xx error body contains a clear message and no stack trace, file path, SQL, or internal field names. CORS is not Access-Control-Allow-Origin: * on this authenticated endpoint.

The point: a status-code-only test passes while the body is wrong, the auth is missing, and internals leak. Status code is the first check, never the only one.
🏗️ Exercise 3 of 3 — Build: a chained API test sequence

A TransitNZ vehicle-registration API supports the full lifecycle of a renewal: POST /renewals, GET /renewals/{id}, PUT /renewals/{id}, DELETE /renewals/{id}. Design a chained test sequence that uses the id returned by the POST in the following requests. List each step, its method, the expected status, and the key assertion. Include at least one negative and one idempotency check.

Show model answer
Chained sequence for a TransitNZ renewal (each step reuses the id from step 1):

Step 1 (create) — POST /renewals with a valid body — 201 Created — body returns a renewalId and status "pending"; capture renewalId.
Step 2 (read) — GET /renewals/{renewalId} — 200 OK — returned record matches what was posted; renewalId matches.
Step 3 (update) — PUT /renewals/{renewalId} with changed details — 200 OK — the changed field is reflected on a follow-up GET.
Step 4 (idempotency) — send the SAME PUT a second time — 200 OK — no duplicate created, same result returned.
Step 5 (negative) — GET /renewals/{someOtherId} you do not own — 403 Forbidden (or 404) — another customer's renewal is never returned.
Step 6 (delete) — DELETE /renewals/{renewalId} — 200 or 204 — a subsequent GET on that id returns 404.

Why chaining matters: hardcoded ids hide sequencing bugs. Feeding the real created id through read, update, delete catches mismatched/zero-padded id formats and state bugs that isolated tests miss.

Why teams fail here

  • They stop at the status code. A suite of 200 green tests that each assert only status == 200 gives the illusion of coverage while the response body is wrong, fields are missing, and another user's data is silently leaking inside a valid-looking response envelope.
  • They treat the UI regression suite as a substitute. The UI only ever sends the requests its own screens generate — always with the logged-in user's own IDs, always with a valid token. Every IDOR, every missing auth check, and every RealMe replay-attack surface exists in the gap between what the UI sends and what the API will accept.
  • They write happy-path tests under time pressure and defer negative tests to "later." In NZ government and finance, the deferred negatives are the auth-missing and IDOR tests — the ones that, when they're finally found in production, generate Privacy Commissioner notifications and Revenue NZ data-breach headlines. Happy-path shortcuts are recoverable; authorisation shortcuts are not.
  • They assume gateway-level auth is enough. In AoG API gateway architectures and RealMe-integrated services, the gateway validates the token but the downstream service must still verify that the authenticated subject owns the resource being requested. Teams that test only at the gateway boundary miss every IDOR that lives one hop behind it.

Key takeaway

The status code is your first assertion, never your only one — an API that returns 200 with the wrong data, a missing field, or another user's record is broken in exactly the way that causes breach headlines, and it will never show up in a suite that only checks for green.

Enterprise reality

300+ microservices, multiple teams, continuous deployment pipelines

  • Contract testing replaces manual API checks at scale — every consumer team owns its Pact files and the provider verifies them in CI, so breaking changes are caught before deployment rather than during integration sprints.
  • API schema registries (Confluent, AWS Glue, internal OpenAPI hubs) become the single source of truth for test generation — tools auto-generate baseline test cases from the schema, and any drift between the registry and the live service is flagged automatically.
  • Breaking change detection is automated in the CI pipeline, not caught in UAT — tools like Optic or Speakeasy diff each PR’s OpenAPI spec against the previous version and block merges that introduce backwards-incompatible changes.
  • API test suites are versioned and owned alongside the services they test — each microservice team maintains its own collection in the service repo, and platform teams own the cross-service contract and integration suites, so there is no single “QA team” bottleneck across 300 services.

How this has changed

The field moved. Here is how API Testing evolved from its origins to current practice.

1990s

SOAP/XML-RPC era. APIs are formal contracts with WSDL schemas. Testing is limited to enterprise teams with dedicated integration testers. Tools are expensive and specialised.

2006

REST APIs popularised by Twitter, Amazon S3, and others. Simpler HTTP-based interfaces democratise API testing. SoapUI becomes the dominant tool for both SOAP and early REST testing.

2012

Postman launches as a Chrome extension. API testing becomes accessible to anyone who can use a browser. The manual-first, explore-then-automate workflow that most teams still use today is established.

2015–19

OpenAPI/Swagger specification adoption enables contract-based testing. Pact consumer-driven contracts emerge. Newman, RestAssured, and pytest-requests enable API test automation in CI pipelines without specialist tooling.

Now

GraphQL, gRPC, WebSocket, and event-driven APIs require testing strategies beyond HTTP request/response. AI systems expose APIs that return non-deterministic outputs — requiring new evaluation approaches. Contract testing is a first-class practice in microservices architectures.

Self-Check

Click each question to reveal the answer.

Q1: Why can a defect be invisible to UI testing but obvious in an API test?

The UI only ever sends the requests its own screens generate — usually the happy path for the logged-in user. Authorisation gaps, validation holes, and wrong data types live in the endpoint and are only exposed by sending requests the UI never would, such as another user’s id or a missing auth token.

Q2: What is the difference between 401 and 403, and between 400 and 422?

401 means not authenticated (no or invalid token); 403 means authenticated but not allowed (you are who you say, but you cannot have this resource). 400 means the request is malformed (bad JSON, wrong type); 422 means the request is well-formed but semantically invalid (e.g. an invalid NZ postcode or Revenue NZ number).

Q3: Beyond the status code, what should a thorough API test assert?

The response body schema (all expected fields present, no unexpected ones), data types (price is a number not a string), required fields are never null, error messages are clear and free of internal details, and the response time meets the SLA.

Q4: Why treat the OpenAPI/Swagger spec as the test oracle?

Consumers — the frontend, mobile app, and partner integrations — code to the spec, not to whatever the API happens to return today. If the live API drifts from the spec, that is a bug even when the API appears to work, because every client built against the contract will silently break.

Q5: A 500 response includes a Java stack trace and a file path. Why is that more than a cosmetic problem?

It is a security defect. Leaked stack traces, file paths, SQL, and internal field names hand an attacker a map of the system’s internals. Error bodies should carry a clear, generic message and nothing about the implementation.

Q6: Your team is testing the Benefits NZ Jobseeker Support API, which has a GET /clients/{clientId}/entitlements endpoint. The frontend always sends the logged-in client’s own id. What additional tests would you run at the API level, and why?

A: First, send a request using another client’s id while authenticated as a different client — this checks for an IDOR vulnerability. Second, send the request with no Authorization header to confirm 401 is returned. Third, send with a valid token but a role that should not have access (e.g. a case worker token that should not see raw entitlement amounts) to confirm 403. The frontend never performs these tests because it only ever requests the current user’s own data. In an Benefits NZ context a data exposure bug like this could breach the Privacy Act 2020 and has regulatory consequences beyond a standard defect.

Q7: What is the key difference between API testing and contract testing, and when would you use each?

A: API testing verifies that a running endpoint behaves correctly right now — you send requests and assert responses against a spec or expected behaviour. Contract testing verifies that the agreed interface between a consumer (e.g. a frontend or mobile app) and a provider (the API) does not break as both sides evolve independently; it uses recorded consumer expectations (Pact files) rather than a live running service. Use API testing to validate correctness and security of a deployed endpoint; use contract testing in CI to catch breaking changes before deployment, particularly in microservice architectures where teams release independently.

Q8: When should you NOT write API tests, even if the system has APIs?

A: Avoid writing API tests when there is no defined contract (no spec, no schema, no agreed behaviour) — without a test oracle you are only recording current behaviour, not verifying correctness. Also skip them when the endpoint is already fully covered by contract tests (Pact/consumer-driven), as duplication costs more to maintain than it gains. Finally, do not write API tests to verify UI-only concerns like layout, field labels, or navigation flow — those are browser-level questions that API calls cannot answer. Time spent on pointless API tests is time stolen from negative and authorisation tests that actually matter.

Q9: A developer says “We don’t need to test the RealMe identity API directly — our login screen already tests it end-to-end whenever we run the UI regression suite.” What is wrong with this, and how do you respond?

A: The UI regression suite tests exactly one call path: the screen’s normal login flow with a valid, authenticated user. It never tests what happens when the Authorization header is absent, when an expired token is replayed, when the identity assertion references a different user’s NHI or RealMe id, or when a malformed request is sent directly. These are precisely the scenarios that cause identity-spoofing and session-fixation bugs. In a RealMe integration — used for government service access across Revenue NZ, CoverNZ, Benefits NZ, and HealthNZ — a missed IDOR or replay-attack vulnerability has regulatory and legal consequences well beyond a cosmetic defect. UI testing and API testing are complementary: the UI tests the presentation layer, the API tests the trust boundary.

Interview Questions

What NZ hiring managers ask about API Testing — and what strong answers look like at each level.

Q: What is API testing and why do we use it instead of just testing through the UI?

Strong answer: API testing means sending HTTP requests directly to backend endpoints and asserting the status code, response body, and behaviour — bypassing the browser entirely. We use it because the business logic, authorisation rules, and data validation all live in the API, not the UI layer. A UI test only ever sends the requests the frontend generates; API testing lets you send requests the UI never would, which is where the most serious defects hide.

Grad / Junior

Q: What is the difference between a 401 and a 403 response, and why does it matter in practice?

Strong answer: A 401 Unauthorised means the request carries no valid identity — no token, or the token is expired or invalid. A 403 Forbidden means the server knows who you are but won’t let you have that resource. The practical difference matters enormously in NZ government and finance systems: if a call to Revenue NZ’s GET /returns/{id} with a valid but wrong-user token returns 200 instead of 403, that is a critical IDOR bug that could expose another taxpayer’s data and breach the Privacy Act 2020.

Grad / Junior

Q: Walk me through how you would test a new POST /payments endpoint on a KiwiSaver provider API. What scenarios would you cover?

Strong answer: I start with the auth test before anything else — remove the Authorization header and confirm I get 401. Then the happy path: valid NZD payment body, expect 201 Created, assert the full response schema (paymentId as a string, amount as a number, currency as “NZD”, status as a valid enum). Then negative paths: missing required fields (400), invalid NZD amount format (422), USD currency rejected (400/422), another member’s account id (403 IDOR check). Finally I check the error bodies on every 4xx to confirm no stack traces or internal field names leak — in a KiwiSaver context that would be a Privacy Act 2020 issue on top of a security defect.

Senior

Q: Your team is under time pressure and wants to skip the negative test cases for the new CoverNZ claims API. How do you respond?

Strong answer: I’d push back specifically on the authorisation tests and keep those non-negotiable. Happy-path cuts are a reasonable time-pressure trade-off; skipping the test where you remove the auth header and the test where you request another claimant’s claim id is how CoverNZ data exposures happen. I’d frame it in risk terms: one IDOR or missing auth check on a claims API could expose sensitive injury and health data, trigger a Privacy Commissioner investigation, and dominate the release headlines. If the team truly can’t fit all negatives, I’d propose a ranked list — auth-missing and IDOR first, then input validation, then edge cases — and document the deferred risks explicitly for the risk register.

Senior

Q: When would you NOT write API tests for a system that has APIs, and how do you decide?

Strong answer: Three situations: when there is no defined contract (no OpenAPI spec, no agreed schema) — without a test oracle you are just recording current behaviour, not verifying correctness; when the endpoint is already fully covered by Pact or another consumer-driven contract test, where duplication costs more to maintain than it gains; and when the concern is purely UI-level (layout, accessibility, navigation) which API calls cannot answer. I use the decision as a conversation with the team — if we have no spec, the first investment is getting the contract defined, because API tests without a test oracle are expensive noise.

Senior

Q: How would you build API testing capability on a team that currently has none — and how would you measure whether it is actually working?

Strong answer: I’d start by picking one high-risk endpoint (typically the first authenticated resource endpoint) and building a reference collection in Postman that covers auth-missing, IDOR, happy-path, and one negative — then walking the team through it in a 30-minute session so the pattern is visible and repeatable. I’d get that collection committed to the repo and wired into CI so it runs on every PR. For measuring effectiveness: track the number of auth and IDOR defects found in API testing vs. those found in UAT or production (shift-left ratio), average time to detect a contract break (should drop once collections run in CI), and the percentage of endpoints that have at least one negative test. For a TransitNZ or Benefits NZ-scale system I’d also require a quarterly review of which endpoints still have no negative coverage and treat that as a risk backlog item.

Lead

What I would do

Professional judgment — when to reach for API Testing, when to skip it, and what to watch for.

If…
I am onboarding onto a system that has an OpenAPI spec and at least one authenticated endpoint — like an CoverNZ claims API or an Revenue NZ myIR integration — and no existing API test suite exists.
I would…
Start with one endpoint, write four tests — auth-missing (401), happy path (assert full schema), IDOR (403), and one invalid-input negative (400/422) — commit that collection to the repo, wire it into CI, and use it as the pattern all new endpoints are expected to follow. Quantity comes after the pattern is established; without the pattern, a large collection of status-code-only tests is just expensive noise.
If…
The team is under time pressure and wants to skip negative tests for a TransitNZ or Benefits NZ API before a release.
I would…
Negotiate, not concede. I’d keep auth-missing and IDOR tests non-negotiable — those are the two that cause breach headlines, Privacy Act 2020 notifications, and Privacy Commissioner investigations. Input-validation negatives (400/422) can be deferred and documented in the risk register. Happy-path shortcuts are a reasonable trade-off; authorisation shortcuts are not. I would put that position in writing before the release.
If…
There is no OpenAPI spec, no agreed schema, and the developer says “just test what it does.”
I would…
Pause and use exploratory API testing to document what the endpoint currently does — record the request, the response shape, the status codes — and turn that into a draft spec. Then get the developer to sign off on it as the agreed contract, and write tests against that. Writing automated API tests against an undefined contract produces tests that assert only that the API does today what it did yesterday — they catch nothing except regressions to undocumented behaviour. That is a test suite with the shape of coverage but none of the substance.

The bottom line: API testing is not about proving the happy path works — the developers already tested that — it is about finding the requests the UI will never send, and the authorisation gaps that only show up when someone who should not have access tries anyway.

Go Deeper

This technique is foundational. Once you understand it, these specialised tracks take you into real-world depth:

📚
API Testing Deep-DiveContract testing, OpenAPI validation, GraphQL, and gRPC