Playwright API Testing with the request Fixture
Playwright is known for browser tests, but its request fixture also sends HTTP requests, so you can test an API from the same project and the same runner. This tutorial covers GET and POST with status and JSON assertions, baseURL, a reusable authentication token, and API setup before a UI check. Every snippet was run on 13 November 2026 with Playwright 1.59.1 against a small local mock API, so none of them depends on a third-party service.
- The request fixture sends GET, POST, PUT and DELETE without opening a browser
- Assert on status and body: toBeOK(), status() and json()
- baseURL needs care: a trailing slash on the base and no leading slash on the path
- API-only tests ran with no browser installed
Why test an API from Playwright
New to Playwright? Start with our beginner's guide or the free PDF course. An API test is faster and less fragile than a browser test, so it is a good place to check business rules. Having it in the same project as your end-to-end tests lets you share configuration, reports and the CI job. It also lets you prepare test data through the API and keep the browser for what only the browser can check.
The request fixture in 10 lines
GET, POST, status and JSON assertions
import { test, expect } from '@playwright/test';
test('read and create a user', async ({ request }) => {
const res = await request.get('users/1');
expect(res.status()).toBe(200);
expect(await res.json()).toMatchObject({ id: 1, name: 'Ada' });
const created = await request.post('users', { data: { name: 'Grace' } });
await expect(created).toBeOK();
expect((await created.json()).name).toBe('Grace');
});
toBeOK() checks that the status is in the 200 range. data sends a JSON body.
baseURL and headers
Set baseURL once in playwright.config.ts so tests use short paths:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: { baseURL: 'http://127.0.0.1:4599/api/' },
});
Authentication: reusing a token across tests
Log in once in a custom fixture, then give each test a ready-to-use context carrying the token:
import { test as base, expect, type APIRequestContext } from '@playwright/test';
export const test = base.extend<{ authedRequest: APIRequestContext }>({
authedRequest: async ({ playwright, baseURL }, use) => {
const login = await playwright.request.newContext({ baseURL });
const res = await login.post('login', { data: { user: 'ada', password: 'secret' } });
const { token } = await res.json();
await login.dispose();
const api = await playwright.request.newContext({
baseURL,
extraHTTPHeaders: { Authorization: 'Bearer ' + token },
});
await use(api);
await api.dispose();
},
});
test('reads the current user', async ({ authedRequest }) => {
await expect(await authedRequest.get('me')).toBeOK();
});
This version logs in for each test. To log in once per worker, declare the fixture with { scope: 'worker' } and read the base URL from an environment variable, as baseURL is a test-scoped option.
Mixing API setup and UI checks
Create the data with the API, then check it in the browser:
test('a user created through the API shows up in the UI', async ({ page, request }) => {
const created = await request.post('api/users', { data: { name: 'Grace' } });
expect(created.status()).toBe(201);
await page.goto('users');
await expect(page.getByText('Grace')).toBeVisible();
});
Common mistakes
- The slash trap. With a baseURL of
http://host/api/,request.get('users/1')reaches/api/users/1, butrequest.get('/users/1')goes to/users/1and drops/api. In our run it returned a 404. Our article on a Claude Code and Playwright test session shows the same trap on a real project. - Shared state. Tests that rely on data created by another test break in parallel runs. Create what each test needs, and clean up.
- Validating the JSON structure. Playwright has no built-in JSON schema validation: use
toMatchObject,expect.any(Number)or a library such as Zod or Ajv. For a broader comparison of API testing tools, see our API testing guide.
Running it in CI
API-only tests do not open a browser: our run passed with no browser installed, so a CI job for them needs Node.js, npm ci and npx playwright test. Add npx playwright install only for the jobs that include UI tests.
Learn Playwright API testing with a trainer
ADC runs two-day weekend courses, 100% online, including a Playwright API topic. Prices are €299 (beginner) and €399 (intermediate) incl. VAT per weekend.
See the weekend coursesOussama Belakhdar
QA Architect · ISTQB CTAL-TM
Founder of AutomationDataCamp: training, consulting and recruitment in software testing. Meet the team →
Frequently asked questions about Playwright API testing
Can Playwright replace Postman for API tests?
For automated checks that run in CI next to your end-to-end tests, yes. Postman remains useful as a client for exploring an API by hand. The choice depends on your team: what matters is that the checks are automated and versioned.
How do I send an authenticated request?
Log in once, read the token from the response, then create a context with extraHTTPHeaders carrying the Authorization header, as in the fixture above.
How do I validate a JSON schema in Playwright?
Playwright has no built-in JSON schema validation. Use toMatchObject or expect.any for simple shapes, or a validation library such as Zod or Ajv for a full schema.
Related articles

Watch Claude Code write a Playwright test from scratch
A real Claude Code session: it explores TodoMVC, writes 3 Playwright tests, hits a baseURL trap, fixes it and checks for flakiness.
Read more : Watch Claude Code write a Playwright test from scratch
Playwright Python tutorial with pytest
From install to CI: your first Playwright tests in Python with pytest, locators, web-first assertions, Codegen, Trace Viewer and API tests...
Read more : Playwright Python tutorial with pytest