When you test through the UI, you're testing the API indirectly: the UI calls the API on your behalf, hiding failure modes that only exist at the HTTP layer. Testing the API directly means sending HTTP requests without a browser, which exposes what happens with missing required fields, invalid auth tokens, or IDs that don't exist. This article covers the full foundation: HTTP request and response structure, what to assert on each response, authentication patterns, and how to write API tests in Playwright.

What an API is from a testing perspective

You can think of an API as a contract. One system says: "Send me a request in this shape, and I'll give you a response in that shape." Your job as a QA engineer is to verify that the contract holds: that the system actually delivers what it promises, rejects what it should reject, and fails gracefully when things go wrong.

In practice, most of the APIs you'll test are REST APIs that communicate over HTTP. A travel booking app has an API endpoint that creates a reservation. A user management system has an endpoint that returns a list of users. An e-commerce platform has endpoints for adding to a cart, checking inventory, and processing payments.

When you test through the UI, you're testing the API indirectly: the UI calls the API on your behalf. When you test the API directly, you remove the UI from the equation entirely. That means faster tests, clearer failure messages, and the ability to test scenarios that the UI doesn't expose: what happens if someone sends a negative price, an empty required field, or a request without authentication?

HTTP fundamentals for testers

Every API interaction is an HTTP request followed by an HTTP response. Here's what each side contains.

A request has four parts: the method, the URL, headers, and an optional body.

The method tells the server what operation you want to perform. The four you'll use constantly are GET (retrieve data), POST (create something new), PUT or PATCH (update something), and DELETE (remove something). Sending a GET to /api/users returns a list of users. Sending a POST to /api/users with user data in the body creates a new user.

The URL is the address of the resource. It usually has a base like https://api.example.com, a path like /users/42, and sometimes query parameters like ?status=active&page=2.

Headers carry metadata: the content type of the body (Content-Type: application/json), authentication credentials (Authorization: Bearer eyJ...), and instructions to the server about what format the client accepts (Accept: application/json).

The body contains the data you're sending, typically as JSON for modern REST APIs.

A response mirrors this structure. It has a status code, headers, and a body. The status code is the most important part for testing:

  • 200 OK: request succeeded, here's what you asked for
  • 201 Created: something was created successfully
  • 400 Bad Request: your request was malformed or missing required fields
  • 401 Unauthorized: you're not authenticated
  • 403 Forbidden: you're authenticated but not allowed to do this
  • 404 Not Found: that resource doesn't exist
  • 422 Unprocessable Entity: the request was well-formed but semantically invalid
  • 500 Internal Server Error: the server broke
The difference between 401 and 403 trips up a lot of testers. 401 means "I don't know who you are, please authenticate." 403 means "I know exactly who you are, and you're not allowed to do this." Always verify which one the API returns for each scenario.

What to test in an API

The instinct is to test only the happy path: send valid data, get a 200 back, done. That approach catches maybe 20% of the bugs. Here's a more complete framework.

Happy paths verify that the API does what it's supposed to do when used correctly. POST to create a user with valid data should return 201 and the created user object. GET for that user's ID should return 200 and the same data. Error cases are where APIs are most commonly broken. What happens when a required field is missing? When the ID in the URL doesn't exist? When the user is not authenticated? Each of these should return a specific status code and a meaningful error message. Test that they do. Edge cases probe the boundaries of what the API accepts. What happens with an empty string where text is expected? A zero or negative number for a quantity? A string that's exactly one character over the maximum length? A date in the past for a "future events only" endpoint? Security basics belong in every API test suite, even at the beginner level. Can you access another user's data by guessing their ID? Does the API accept requests without authentication? Does it return more data in the response than the client should see (over-fetching)? These aren't penetration tests, just sanity checks.

A useful mental model: for every endpoint, ask yourself three questions. What should work? What should fail? What should the error look like when it fails?

Tools: Postman, Playwright, and curl

Three tools cover most of what you need.

curl is the quickest way to make a one-off HTTP request from the terminal. You don't need to install anything; it's already on every developer's machine. Use it for quick checks during exploration:

# GET request
curl https://lab.becomeqa.com/api/items

# POST with a JSON body
curl -X POST https://lab.becomeqa.com/api/items \
  -H "Content-Type: application/json" \
  -d '{"destination": "Tokyo", "status": "planned"}'

# With an Authorization header
curl https://lab.becomeqa.com/api/items \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

Postman is your exploration tool. Use it when you're working with an API for the first time: testing endpoints manually, inspecting responses, figuring out what parameters are required. Postman's collection runner can run a sequence of requests and serves as a scratch pad before you write actual automated tests. Think of it as a REPL for APIs. Playwright is your automation tool. It has a built-in HTTP client that lets you write API tests in the same framework as your UI tests. The request fixture works without opening a browser, runs in milliseconds, and integrates with your existing test reports:

import { test, expect } from '@playwright/test';

test('POST /api/items creates a new item', async ({ request }) => {
  const response = await request.post('https://lab.becomeqa.com/api/items', {
    data: {
      destination: 'Tokyo',
      status: 'planned'
    }
  });

  expect(response.status()).toBe(201);

  const created = await response.json();
  expect(created).toHaveProperty('id');
  expect(created.destination).toBe('Tokyo');
});

The workflow is: explore with Postman and curl, automate with Playwright.

REST API testing anatomy

A good API test checks more than just the status code. Here's what to assert on every response.

The status code is the first gate. Get it right before asserting anything else. If the status is wrong, everything else is irrelevant.

The response body structure matters. If the API promises to return a user object with id, email, and role, your test should verify that all three fields are present on every response. This catches silent regressions where someone adds a required field to the front-end contract but forgets to include it in the API response.

Field values matter, not just field existence. If you created a user with email test@example.com, the response should reflect that exact email. If the API returns a status field, verify it's one of the expected values, not just any string.

Response headers carry information too. Check Content-Type: application/json if you're expecting JSON. For endpoints that return paginated data, check for pagination headers like X-Total-Count or links in the response body.

Response time is a signal. Playwright doesn't give you assertions on response time out of the box, but you can measure it manually. An endpoint that normally responds in 50ms returning in 2000ms is worth flagging even if the data is correct.

Authentication in API tests

Almost every production API requires authentication. The two patterns you'll encounter daily are API keys and Bearer tokens.

API keys are static strings that identify a client application. They're usually sent as a header:

curl https://api.example.com/data \
  -H "X-API-Key: your-api-key-here"

In your automated tests, never hardcode the key. Store it in an environment variable and read it from there. This keeps secrets out of your test code and makes it easy to switch between staging and production credentials.

Bearer tokens are dynamic. They expire, and you usually get one by calling a login endpoint first. The pattern in your tests is always the same: call the auth endpoint, extract the token, pass it in the Authorization header of subsequent requests.
Always test the unhappy auth paths explicitly. What does the API return when you send no token? An expired token? A token that was issued for a different user? These should return 401, not 500, and definitely not 200 with someone else's data. A 200 response on an unauthenticated request is a security bug.

Testing auth-related edge cases (invalid tokens, missing tokens, tokens with wrong permissions) is some of the highest-value API testing you can do. The consequences of getting auth wrong are severe, and these bugs often live in the API, not in the UI where your UI tests would catch them.

Where API testing fits in the test pyramid

The test pyramid is a model for how to distribute your tests. At the base: many unit tests, fast and cheap, testing individual functions. In the middle: integration and API tests, testing how systems talk to each other. At the top: few UI tests, testing complete user journeys.

API tests sit in the sweet spot. They're significantly faster than UI tests. A typical API test runs in 50–200 milliseconds versus 3–10 seconds for a UI test. They're more stable because they don't break when a button label changes or a CSS selector shifts. And they test the layer where most backend bugs actually live.

A practical split for a typical web application: for every 1 UI test covering a user flow, write 3–5 API tests covering the underlying endpoints. The UI test verifies that the pieces connect correctly. The API tests verify that each piece works correctly on its own.

This matters most for business logic. If an e-commerce app calculates discounts on the backend, test that calculation via the API, not by filling out a checkout form in a browser. The API test will be ten times faster and will test every edge case (zero items, expired coupon codes, stacked discounts) without any of the UI fragility.

How to apply this Monday morning

You don't need to refactor your entire test suite. Here's how to start small this week.

Pick one feature that has UI tests but no API tests. Look at the network traffic in Chrome DevTools while using that feature. You'll see the API calls the UI makes. Pick the most important endpoint (usually the one that creates or retrieves the core data) and write one API test for it in Postman first.

Verify the happy path. Then write one test for a 404 (request a resource by a non-existent ID). Then write one test for a 400 (send a POST with a missing required field). Three tests that take 30 minutes to write, and you've established the pattern.

Next, move those three tests into Playwright so they run in CI alongside your existing tests. Use the code structure from the examples in this article. Run them once to confirm they pass.

From there, expand coverage by asking: "What's the next most important endpoint?" and "What error cases does this endpoint have that I'm not testing?"

Within a few weeks, you'll have an API test layer that catches backend bugs in seconds instead of letting them surface through UI tests that run for minutes. That's the practical payoff of treating API testing as a first-class activity, not a fallback when the UI breaks.

→ See also: API Testing with Playwright: Beyond the UI | What Is a REST API? A Practical Guide for QA Engineers | HTTP Status Codes Every QA Engineer Needs to Know | Postman for QA Engineers: From First Request to API Test Suite | QA Automation Roadmap 2026: Essential Skills to Get Hired