Every modern application has two sides: the user interface you can see, and the API behind it that moves the data. API tests talk to that second side directly. They are faster than UI tests, more stable, and they find bugs earlier. That is why almost every QA automation job asks for API testing skills.
This guide explains the basics in simple terms, with code examples.
What is an API?
An API (Application Programming Interface) is how programs talk to each other. When you press "Sign in" in a web app, the frontend sends a request like this to the backend:
POST /api/login
Content-Type: application/json
{ "email": "demo@nimbus.app", "password": "secure123" }
And the backend answers:
200 OK
Content-Type: application/json
{ "token": "eyJhbGci...", "user": { "name": "Alex" } }
API testing means sending these requests yourself and checking the answers, without any browser.
HTTP methods
| Method | Meaning | Example |
|---|---|---|
| GET | Read data | Get a list of contacts |
| POST | Create data | Create a new contact |
| PUT / PATCH | Update data | Change a contact's email |
| DELETE | Remove data | Delete a contact |
Status codes you must know
- 200 OK, request worked.
- 201 Created, resource was created (common for POST).
- 400 Bad Request, your request data was wrong.
- 401 Unauthorized. You are not logged in / token missing.
- 403 Forbidden, logged in, but not allowed.
- 404 Not Found, resource does not exist.
- 500 Internal Server Error. The server crashed. Almost always a bug.
A good API test checks the status code and the response body.
What to test
For an endpoint like POST /api/contacts, a solid test list looks like this:
- Happy path, valid data creates a contact, returns 201 and the new contact with an id.
- Validation, missing name returns 400 with a clear error message.
- Authentication, request without a token returns 401.
- Not found, updating a contact that does not exist returns 404.
- Data check, after creating, a GET returns the same contact.
Notice the pattern: one happy path, then the important ways it can fail.
Writing an API test in code
Playwright has a built-in HTTP client, so you can write API tests in the same project as UI tests:
import { test, expect } from '@playwright/test';
test('creates a contact', async ({ request }) => {
const response = await request.post('/api/contacts', {
data: { name: 'Maya Chen', email: 'maya@lumina.io', company: 'Lumina' },
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body.name).toBe('Maya Chen');
expect(body.id).toBeDefined();
});
test('rejects a contact without a name', async ({ request }) => {
const response = await request.post('/api/contacts', {
data: { email: 'no-name@test.io' },
});
expect(response.status()).toBe(400);
const body = await response.json();
expect(body.error).toContain('name');
});
The structure is the same as a UI test: send, then assert. There are no waits and no selectors, which is why API tests are so stable.
Authentication in tests
Most APIs need a token. The usual pattern: log in once, save the token, send it in a header:
const login = await request.post('/api/login', {
data: { email: 'demo@nimbus.app', password: 'secure123' },
});
const { token } = await login.json();
const contacts = await request.get('/api/contacts', {
headers: { Authorization: `Bearer ${token}` },
});
expect(contacts.status()).toBe(200);
Common mistakes
- Only testing the happy path. Most bugs live in error handling. Always test bad input and missing auth.
- Checking only the status code. A 200 with a wrong body is still a bug. Assert on the data.
- Tests that depend on each other. If test B needs the contact from test A, one failure breaks both. Each test should create its own data.
- Hardcoding tokens. Tokens expire. Log in inside the test setup instead.
- Ignoring response time. An endpoint that answers in 8 seconds passes a normal assertion but fails your users.
Best practices
- Test APIs below the UI first. If the API is broken, UI tests just fail with confusing messages.
- Keep one request per helper function, so a URL change means one fix.
- Use clear test names:
'returns 401 without a token'. - Combine both layers: create data through the API, then verify the UI shows it. This is the fastest stable pattern for end-to-end coverage.
Next steps
Learn how UI and API testing fit together in the QA automation roadmap, then practice UI test writing in the the practice editor. For interview questions about API testing, see API testing interview prep.