Playwright is best known as a browser automation framework. But since version 1.16, it ships with a full APIRequestContext — a native HTTP client you can use to test REST APIs directly, no browser required. This makes it possible to combine UI and API tests in a single framework and a single test run.
Setting Up
Install Playwright and initialise a project:
# Install Playwright npm init playwright@latest # Or add to an existing project npm install --save-dev @playwright/test
Create a file tests/api.spec.ts — this is where your API tests will live.
Your First API Test
Playwright exposes request as a fixture inside test blocks. Here's a simple GET request test against a public API:
import { test, expect } from '@playwright/test'; test('GET /users returns 200', async ({ request }) => { const response = await request.get( 'https://jsonplaceholder.typicode.com/users' ); expect(response.status()).toBe(200); const body = await response.json(); expect(body.length).toBeGreaterThan(0); });
Run it with:
npx playwright test tests/api.spec.ts
POST, PUT, DELETE
The API is consistent across all HTTP methods:
// POST — create a resource const res = await request.post('/api/posts', { data: { title: 'Hello World', body: 'Content here', userId: 1 } }); expect(res.status()).toBe(201); // PUT — update a resource const updated = await request.put('/api/posts/1', { data: { title: 'Updated title' } }); expect(updated.ok()).toBeTruthy(); // DELETE const deleted = await request.delete('/api/posts/1'); expect(deleted.status()).toBe(200);
Authentication
Pass headers directly on each request or set them globally via extraHTTPHeaders in your Playwright config:
// playwright.config.ts export default { use: { baseURL: 'https://api.yourapp.com', extraHTTPHeaders: { 'Authorization': `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json', } } };
Combining UI and API Tests
The real power comes when you mix both. Use API calls to set up test state fast, then verify the result in the browser — no slow UI setup flows:
test('created post appears in UI', async ({ page, request }) => { // Create data via API (fast) await request.post('/api/posts', { data: { title: 'My Test Post' } }); // Verify in the browser (UI) await page.goto('/posts'); await expect(page.locator('text=My Test Post')) .toBeVisible(); });
Key Takeaways
- Playwright's request fixture gives you a full HTTP client with zero extra dependencies
- All HTTP methods are supported — GET, POST, PUT, PATCH, DELETE
- Set shared headers globally in playwright.config.ts
- Mix API setup with UI assertions in the same test for fast, reliable e2e coverage
- Run API-only tests headlessly — they're much faster than browser tests
What's Next
Once you're comfortable with basic requests, explore Playwright's APIRequestContext for session management, response body schema validation with libraries like Zod or Joi, and parallel test execution with Playwright's built-in sharding.
Check out our full Playwright course for a complete walkthrough from setup to CI/CD integration.