When a UI shows wrong data, the Network tab in DevTools tells you whether the API returned wrong data (backend bug) or the right data was displayed incorrectly (frontend bug). That single diagnostic habit requires understanding how REST requests and responses are structured. This article covers HTTP methods, status codes (including why a successful POST should return 201 not 200), authentication headers, and how to make direct API calls in Playwright tests.
The core concept: request and response
An API (Application Programming Interface) is a way for two pieces of software to talk to each other. A REST API is a specific style of API that uses HTTP — the same protocol your browser uses to load websites.
The conversation always goes the same way:
1. Your code (or test) sends a request
2. The server processes it and sends back a response
That's it. Everything else is just details about how those two things are structured.
Anatomy of an HTTP request
Every request has four parts:
1. Method (the verb)
The method tells the server what you want to do:
| Method | What it does | Like saying |
|--------|-------------|-------------|
| GET | Fetch data | "Give me this thing" |
| POST | Create something new | "Here's new data, save it" |
| PUT | Replace something entirely | "Replace this with my new version" |
| PATCH | Update part of something | "Change just this one field" |
| DELETE | Remove something | "Delete this" |
A bug report endpoint might behave like:
GET /bugs→ list all bugsGET /bugs/42→ get bug #42POST /bugs→ create a new bugPATCH /bugs/42→ update the status of bug #42DELETE /bugs/42→ delete bug #42
2. URL (the address)
The URL tells the server what you're working with:
https://api.becomeqa.com/v1/users/123/ordersBreaking this down:
https://api.becomeqa.com: the server/v1: API version (common, not always present)/users/123: the specific user with ID 123/orders: their orders
The parts after the domain are called the path. Numbers and IDs in the path (like 123) are called path parameters.
Sometimes data is passed in the URL as query parameters:
GET /users?role=admin&active=true&page=2The ?role=admin&active=true&page=2 part filters and paginates the results without changing what resource you're talking about.
3. Headers
Headers are metadata sent with the request. You can't see them in the browser normally (they're in the Network tab). Common ones:
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/jsonContent-Typetells the server what format your request body is inAuthorizationproves who you are (more on this below)Accepttells the server what format you want the response in
4. Body
POST, PUT, and PATCH requests usually include a body — the actual data being sent. REST APIs almost always use JSON:
{
"title": "Login button not working on mobile",
"severity": "high",
"steps": ["Open app on mobile", "Tap Login", "Nothing happens"]
}GET and DELETE requests typically have no body.
Anatomy of an HTTP response
The server responds with:
1. Status code
A 3-digit number that tells you what happened:
| Range | Category | Meaning |
|-------|----------|---------|
| 2xx | Success | Request worked |
| 3xx | Redirect | Resource moved, go here instead |
| 4xx | Client error | Your request is wrong |
| 5xx | Server error | Server broke |
The most important codes for testing:
| Code | Name | When it appears |
|------|------|----------------|
| 200 | OK | Standard success for GET |
| 201 | Created | POST that created something |
| 204 | No Content | Success but nothing to return (DELETE) |
| 400 | Bad Request | Invalid input (missing field, wrong format) |
| 401 | Unauthorized | Not authenticated (no/bad token) |
| 403 | Forbidden | Authenticated but not allowed |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable Entity | Input is valid JSON but fails validation |
| 429 | Too Many Requests | Rate limited |
| 500 | Internal Server Error | Unhandled backend crash |
| 503 | Service Unavailable | Server is down or overloaded |
2. Response headers
Similar to request headers — metadata about the response:
Content-Type: application/json
X-Request-Id: abc-123-def
Cache-Control: no-cache3. Response body
The actual data returned, usually JSON:
{
"id": 42,
"title": "Login button not working on mobile",
"severity": "high",
"status": "open",
"created_at": "2026-05-17T10:30:00Z"
}Or an error:
{
"error": "validation_failed",
"message": "Title is required",
"field": "title"
}Authentication: how APIs know who you are
Most APIs require authentication. There are two main approaches you'll encounter:
Bearer Token / JWT
After login, the server gives you a token — a long string that proves your identity. You include it in every subsequent request:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIn0.abc123The server validates the token without needing to look up your session in a database. Tokens expire (usually 15 minutes to 24 hours).
Cookie-based sessions
Older pattern. After login, the server sets a cookie. The browser automatically sends it with every request to that domain. You don't see it in the request body — the browser handles it.
API keys
Used for server-to-server communication. A static key in the header:
X-API-Key: sk-1234567890abcdefHow to inspect API calls in a browser
Open DevTools → Network tab → filter by "Fetch/XHR." Every API call your app makes will appear here.
Click any request to see:
- Headers tab: request headers, URL, method
- Payload tab: request body
- Response tab: what the server returned
- Status code: in the list view, the number column
This is where you debug "is this a frontend bug or a backend bug?" If the UI shows wrong data, check what the API actually returned. If the API returned the right data and the UI shows it wrong, it's frontend. If the API returned wrong data, it's backend.
Testing APIs: what to check
When testing an API endpoint (whether manually or with Playwright), verify:
Status code matches expectation
- Creating a resource →
201, not200 - Getting a resource →
200 - Empty successful delete →
204 - Invalid input →
400or422, not500
Response body is correct
- Required fields are present
- Data types are right (
"age": 25not"age": "25") - Values match what was sent
- No extra sensitive data leaked (passwords, internal IDs)
Error responses are useful
400errors should explain what was invalid404errors should confirm the resource doesn't exist401errors should not reveal security-sensitive info
Edge cases behave correctly
- Empty list: returns
[]notnull - Non-existent ID: returns
404not500 - Invalid JSON body: returns
400not500 - Unauthorized action: returns
403not200with empty body
REST APIs in Playwright
Playwright can make HTTP requests directly, without a browser:
test('creating a user returns 201', async ({ request }) => {
const response = await request.post('https://api.becomeqa.com/users', {
data: {
name: 'Test User',
email: 'test@example.com',
role: 'member',
},
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body.id).toBeTruthy();
expect(body.email).toBe('test@example.com');
expect(body).not.toHaveProperty('password'); // no leaking
});You can also intercept API calls made by the browser during E2E tests:
test('handles API error gracefully', async ({ page }) => {
// Force the API to return an error
await page.route('**/api/users', (route) => {
route.fulfill({ status: 500, body: JSON.stringify({ error: 'server error' }) });
});
await page.goto('/users');
// App should show error state, not crash
await expect(page.locator('[data-testid="error-banner"]')).toBeVisible();
});Key vocabulary summary
| Term | Meaning |
|------|---------|
| API | Interface for software to communicate |
| REST | A style of API that uses HTTP |
| Endpoint | A specific URL + method combination (GET /users) |
| Request | What you send to the server |
| Response | What the server sends back |
| Status code | 3-digit number indicating success or failure |
| JSON | The data format REST APIs use (text-based, key-value pairs) |
| Header | Metadata attached to a request or response |
| Body | The actual data in a request or response |
| Bearer token | Credential sent in Authorization header |
| Path parameter | Variable part of the URL (/users/123, where 123 is the param) |
| Query parameter | Filters in the URL after ? (?page=2&sort=asc) |
Understanding REST APIs makes you a significantly better QA engineer. You can read network traffic, diagnose whether a bug is frontend or backend, write API-level tests that run 100x faster than browser tests, and have much more meaningful conversations with developers when you file bugs.
→ See also: API Testing 101: What Every QA Engineer Needs to Know in 2026 | HTTP Status Codes Every QA Engineer Needs to Know | API Testing with Playwright: Beyond the UI | How the Internet Works for Testers