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 bugs
  • GET /bugs/42 → get bug #42
  • POST /bugs → create a new bug
  • PATCH /bugs/42 → update the status of bug #42
  • DELETE /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/orders

Breaking 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=2

The ?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/json

  • Content-Type tells the server what format your request body is in
  • Authorization proves who you are (more on this below)
  • Accept tells 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-cache

3. 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.abc123

The 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-1234567890abcdef

How 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, not 200
  • Getting a resource → 200
  • Empty successful delete → 204
  • Invalid input → 400 or 422, not 500

Response body is correct

  • Required fields are present
  • Data types are right ("age": 25 not "age": "25")
  • Values match what was sent
  • No extra sensitive data leaked (passwords, internal IDs)

Error responses are useful

  • 400 errors should explain what was invalid
  • 404 errors should confirm the resource doesn't exist
  • 401 errors should not reveal security-sensitive info

Edge cases behave correctly

  • Empty list: returns [] not null
  • Non-existent ID: returns 404 not 500
  • Invalid JSON body: returns 400 not 500
  • Unauthorized action: returns 403 not 200 with 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