Intermediate Chapter 12 · 14 min read

API Testing with Playwright

Send HTTP requests directly from Playwright tests and combine API calls with UI interactions for powerful end-to-end flows.

Why Test APIs with Playwright?

Playwright includes a built-in HTTP client — the APIRequestContext — that shares the browser's cookie jar and authentication state. This means you can log in once via the API and immediately use the resulting session in browser tests, eliminating slow UI login flows from every test. You can also use the API client to set up test data before a UI test or to clean up afterwards.

Tip: Use request.storageState() to save authenticated API session state to a file, then load it in browser contexts so that UI tests skip the login page entirely.

Making API Requests

The request fixture (JavaScript/Python) or playwright.request().newContext() (Java) provides get(), post(), put(), patch(), and delete() methods. Each returns a response object with status(), json(), text(), and headers(). The API is promise-based in JavaScript, synchronous in Python, and uses a callback pattern in Java.

Combining API and UI Tests

A common pattern is to create a resource via API (fast), verify it in the UI (user-facing), and then delete it via API (fast cleanup). This approach tests the full system while keeping the majority of operations out of the slow browser path, significantly reducing total test runtime in large suites.

api.spec.js
import { test, expect } from '@playwright/test';

test.describe('Users API', () => {
  let createdUserId;

  test('POST /api/users creates a user', async ({ request }) => {
    const response = await request.post('/api/users', {
      data: { name: 'Alice', email: 'alice@example.com', role: 'editor' },
    });
    expect(response.status()).toBe(201);
    const body = await response.json();
    expect(body.name).toBe('Alice');
    createdUserId = body.id;
  });

  test('GET /api/users returns list', async ({ request }) => {
    const response = await request.get('/api/users');
    expect(response.ok()).toBeTruthy();
    const users = await response.json();
    expect(Array.isArray(users)).toBeTruthy();
  });

  test('API create then verify in UI', async ({ request, page }) => {
    // Fast: create data via API
    await request.post('/api/products', {
      data: { name: 'Test Widget', price: 9.99 },
    });

    // Slow: verify it appears in the UI
    await page.goto('/products');
    await expect(page.getByText('Test Widget')).toBeVisible();

    // Fast: clean up via API
    await request.delete('/api/products/test-widget');
  });
});
ApiTest.java
import com.microsoft.playwright.*;
import com.google.gson.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

class ApiTest {
  void testUserApi(Playwright playwright) {
    APIRequestContext request = playwright.request().newContext(
        new APIRequest.NewContextOptions()
            .setBaseURL("http://localhost:3000")
    );

    // POST — create user
    APIResponse response = request.post("/api/users",
        RequestOptions.create().setData(
            new JsonObject() {{ addProperty("name", "Alice"); }}
        )
    );
    assert response.status() == 201;

    // GET — list users
    APIResponse list = request.get("/api/users");
    assert list.ok();

    // PUT — update
    request.put("/api/users/1",
        RequestOptions.create().setData(
            new JsonObject() {{ addProperty("role", "admin"); }}
        )
    );

    // DELETE — remove
    request.delete("/api/users/1");
    request.dispose();
  }
}
test_api.py
import pytest
from playwright.sync_api import APIRequestContext, Page, expect

@pytest.fixture(scope="session")
def api_context(playwright):
    ctx = playwright.request.new_context(
        base_url="http://localhost:3000"
    )
    yield ctx
    ctx.dispose()

def test_create_user(api_context: APIRequestContext):
    response = api_context.post(
        "/api/users",
        data={"name": "Alice", "email": "alice@example.com"}
    )
    assert response.status == 201
    body = response.json()
    assert body["name"] == "Alice"

def test_api_create_then_verify_ui(
    api_context: APIRequestContext, page: Page
):
    # Fast: create via API
    api_context.post(
        "/api/products",
        data={"name": "Test Widget", "price": 9.99}
    )
    # Verify in UI
    page.goto("/products")
    expect(page.get_by_text("Test Widget")).to_be_visible()
    # Clean up via API
    api_context.delete("/api/products/test-widget")

Playwright Intermediate API Testing with Playwright

Written by PV

© 2026 All Rights Reserved