Your Playwright tests pass. The login button clicks, the dashboard renders, the chart paints. Then a customer reports a bug: the chart numbers are wrong. You dig in and find that the API returned a 200 status with a malformed payload, and your end-to-end suite never noticed because it only checked that pixels appeared on screen. This is the gap that browser tests alone can’t close, and it’s where API assertions become non-negotiable. Tools like Apidog give you a way to validate API contracts, schemas, and response semantics with the same rigor you give your UI flows, then run both suites together in CI.
TL;DR
You can validate APIs in Playwright tests by combining Playwright’s request fixture and page.route interceptor with Apidog scenarios that hit the same OpenAPI spec. Share fixtures through a single spec file, assert response schemas in both layers, and run both suites in one CI job so a contract change fails fast in either place.
Introduction
Playwright is the default browser automation framework for many teams in 2026, and the Playwright documentation makes API testing look easy: a few request.get calls, an expect(response.status()).toBe(200), and you’re done. The trouble starts when you scale that up. You end up with hundreds of tests that check status codes but not response shape, no shared source of truth between your browser flows and your API flows, and no way to mock APIs offline when the backend is slow or broken.
The fix is straightforward. Treat your OpenAPI spec as the contract, drive Playwright request calls and page.route interceptors from that contract, and run an Apidog scenario suite against the same spec for deep schema, business-logic, and chained-request validation. You get fast feedback in CI, clear ownership boundaries between frontend and backend tests, and zero duplicated fixtures. If you want to install the tool first, Download Apidog and come back; the steps below assume it’s available locally.
Here’s what you’ll get out of this post: a clean mental model for where API assertions belong in a Playwright suite, a working request.fixture pattern, a step-by-step setup for sharing fixtures between Playwright and Apidog, advanced tips for CI and mocking, and a comparison table of the main alternatives. For broader context on test tooling choices, see our take on API testing tools for QA engineers.
The gap between Playwright tests and API assertions
A typical Playwright test logs in, navigates to a page, and asserts that some UI element is visible. That tells you the user-visible flow works. It tells you nothing about the API behind it. Three failure modes slip through:
First, payload shape regressions. The endpoint returns 200 with a field renamed from total_count to totalCount. The UI might silently coerce or display zero. Your Playwright test sees a number on screen and passes.
Second, business logic drift. A discount endpoint applies a 10 percent rebate instead of the contracted 15 percent. The UI shows whatever the API returns, so the test passes. Only the finance team notices, weeks later.
Third, error path coverage. Playwright tests almost always run the happy path. Your API has dozens of 4xx and 5xx branches: rate limits, expired tokens, partial failures, idempotency conflicts. None of them get exercised unless you write a separate API test suite.
You can patch some of this by adding request.get calls inside your Playwright specs and asserting response bodies. That works for a handful of endpoints. It falls apart when you have 200 endpoints and want chained scenarios like “create order, fetch order, cancel order, verify refund webhook.” Playwright is a browser automation framework first; it’s not built for stateful API workflows or schema-level assertion ergonomics. That’s where a dedicated API testing tool earns its keep.
The right split is:
- Playwright tests verify UI flows, network interception, and a thin layer of API smoke checks at the boundary of a user action.
- Apidog scenarios verify schema conformance, chained API workflows, contract compliance, and error paths in depth.
Both suites consume the same OpenAPI spec so you never have two versions of truth. For a deeper view on the contract-first approach, our piece on contract-first development tooling walks through why the spec must lead.
How to share fixtures between Playwright and Apidog
The single source of truth is your OpenAPI spec, usually openapi.yaml or openapi.json at the repo root. Playwright reads it for typed request helpers and example payloads; Apidog imports it directly to populate scenario steps. Whenever the backend ships a contract change, both suites pick it up.
Start with a fixtures/ folder that holds reusable test data: users, tokens, sample payloads. Build a Playwright fixture file that loads these and exposes them to tests:
// tests/fixtures/api.ts
import { test as base, APIRequestContext, expect } from '@playwright/test';
import { readFileSync } from 'fs';
import path from 'path';
type ApiFixtures = {
apiRequest: APIRequestContext;
authToken: string;
sampleOrder: Record<string, unknown>;
};
export const test = base.extend<ApiFixtures>({
apiRequest: async ({ playwright }, use) => {
const ctx = await playwright.request.newContext({
baseURL: process.env.API_BASE_URL ?? 'https://api.staging.example.com',
extraHTTPHeaders: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
await use(ctx);
await ctx.dispose();
},
authToken: async ({ apiRequest }, use) => {
const res = await apiRequest.post('/auth/token', {
data: { email: 'qa@example.com', password: process.env.QA_PASSWORD },
});
expect(res.status()).toBe(200);
const body = await res.json();
await use(body.access_token);
},
sampleOrder: async ({}, use) => {
const raw = readFileSync(
path.join(__dirname, '..', '..', 'fixtures', 'order.json'),
'utf8',
);
await use(JSON.parse(raw));
},
});
export { expect };
Now you import test from this fixture file instead of @playwright/test in every spec, and you have a typed apiRequest, a fresh authToken, and sampleOrder data ready to go. The order.json file is the same payload Apidog uses as the example body for POST /orders in your scenarios. Edit it once, both suites change.
For the Apidog side, open the project, click “Import”, and point it at the same openapi.yaml. Apidog generates endpoints, request examples, and parameter schemas in seconds. Then save your fixture payloads as Apidog “Environment Variables” or “Data Sets”:
// tests/orders.spec.ts
import { test, expect } from './fixtures/api';
test('POST /orders returns a valid order with 15 percent discount', async ({
apiRequest,
authToken,
sampleOrder,
}) => {
const res = await apiRequest.post('/orders', {
headers: { Authorization: `Bearer ${authToken}` },
data: { ...sampleOrder, coupon: 'SAVE15' },
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body).toMatchObject({
id: expect.any(String),
status: 'pending',
discount_pct: 15,
total_cents: expect.any(Number),
});
expect(body.total_cents).toBeLessThan(sampleOrder.subtotal_cents);
});
Inside Apidog, the matching scenario step posts the same payload, then runs the built-in JSON Schema check against the Order component in openapi.yaml. That gives you depth: every field is type-checked, required fields are verified, enum values are enforced. Playwright catches the high-value semantic assertion (discount_pct: 15); Apidog catches every field that drifts, even ones you forgot to assert by hand. If you’re new to spec-driven testing, our walkthrough on design-first API workflows shows the surrounding pattern.
For teams that already use Postman and are considering a switch, self-hosted Postman alternatives covers the migration mechanics.
Set up the Apidog + Playwright workflow
Here is a clean, repeatable setup that gets you from zero to dual-suite CI in about an hour.
Step 1: One spec to rule them all. Put openapi.yaml at the repo root. Treat it as code: PR reviews required, breaking changes call for a major version bump. If you don’t have one yet, generate a draft from your existing routes using a framework plugin (FastAPI, NestJS, and others emit OpenAPI natively), then hand-edit. Apidog can also reverse-engineer a spec from traffic if you import a HAR file.
Step 2: Wire Playwright. Install Playwright (npm init playwright@latest) and add the fixture file shown above. Add an npm run test:e2e script and a playwright.config.ts that points at your staging environment. Keep tests small; one scenario per spec.
Step 3: Add the Apidog scenario layer. Inside your Apidog project, import openapi.yaml, then build a scenario per critical user journey: signup, checkout, refund, webhook delivery, and so on. Each scenario is a sequence of API calls with assertions chained together. Apidog supports environment variables, pre-request scripts, and post-response assertions. Export the scenario as a CLI-runnable JSON via the Apidog CLI (apidog-cli run scenario.json).
Step 4: Network interception in Playwright. When the UI fetches data you don’t want to hit live, use page.route to intercept and stub. The stubbed responses come from the same fixture files, so the contract stays consistent:
test('dashboard renders cached order list when offline', async ({
page,
sampleOrder,
}) => {
await page.route('**/api/orders', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ orders: [sampleOrder] }),
});
});
await page.goto('/dashboard');
await expect(page.getByTestId('order-row')).toHaveCount(1);
});
Then in Apidog, run the same GET /orders scenario against your actual backend or against an Apidog mock server. Same fixture, two layers of verification.
Step 5: CI integration. Add a GitHub Actions workflow that runs both suites in parallel:
name: tests
on: [push, pull_request]
jobs:
playwright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
apidog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm i -g apidog-cli
- run: apidog-cli run ./apidog/scenarios/checkout.json --reporters cli,junit
Failing either job blocks the merge. Use --reporters junit so GitHub annotates failed assertions inline on the PR. The GitHub Actions documentation covers matrix builds and caching if you want to scale this out. For teams without a dedicated QA function, the API testing tool for QA engineers walkthrough explains how to assign ownership of each suite.
Step 6: Drift detection. Schedule a daily job that diffs your live openapi.yaml against the version your tests were last run against. If a field changed type, the diff fails the build before any test runs. This catches the “200 OK with wrong payload” class of bug that we opened with.
Advanced techniques and pro tips
A few moves that pay off after the basic setup is running.
Pin the Playwright trace viewer. Set trace: 'on-first-retry' in playwright.config.ts. When a flaky test fails in CI, you get a full timeline of network calls, DOM snapshots, and console logs. Pair it with apidog-cli --report html for the API side. Together they show you whether the UI broke first or the API drifted.
Use Apidog mock servers for offline runs. Apidog can spin up a mock server from your OpenAPI spec in one click. Point your local dev environment at it when the backend team is mid-deploy or the staging database is being reset. Your Playwright suite runs green against the mock, and your Apidog scenarios verify the real backend in parallel. For more on this pattern, see our look at AI-assisted API test generation where mocks are central.
Cap retry counts to two. retries: 2 in playwright.config.ts. If a test needs three retries to pass, it’s flaky and you have a real problem. Don’t paper over it with retries: 5. The same goes for Apidog scenarios: set retry: 1 per request, max.
Fail closed on schema drift. When Apidog detects a schema mismatch, exit non-zero by default. Don’t let a warning slip through. If you must allow a soft-fail window, gate it behind an env var like ALLOW_SCHEMA_DRIFT=true and require a comment on the PR explaining why.
Tag tests by priority. Use Playwright’s test.describe.configure({ mode: 'serial' }) for stateful flows and tag others with @smoke, @regression, @nightly. Run smoke on every push, regression on PRs to main, nightly across the full Apidog scenario suite. Saves CI minutes without losing coverage.
Common mistakes to avoid:
- Asserting only
status === 200. Add at least one body field check. - Hardcoding bearer tokens in fixtures. Use a
beforeAllto fetch fresh tokens. - Letting Playwright and Apidog import different fixture files. One source, period.
- Skipping the Apidog CLI in CI because “the UI tool is good enough.” It isn’t; CLI is what fails the build.
- Treating
page.routestubs as a substitute for real API tests. They’re for isolation, not coverage.
For teams generating tests with AI, the how to test AI agents API guide covers the non-deterministic cases that need extra care.
Alternatives and tooling comparison
Several tool combinations can validate APIs alongside browser tests. Here is how the main options stack up.
| Stack | Strengths | Weaknesses | Best for |
|---|---|---|---|
Playwright alone (request fixture) |
One tool, fast, native to the suite | Shallow schema validation, no chained scenarios, weak error-path coverage | Small teams, simple APIs |
| Playwright + Postman | Mature Postman ecosystem, Newman CLI | Two sources of truth, Postman collections drift from OpenAPI, paid for collaboration | Teams already deep in Postman |
| Playwright + Apidog | Single OpenAPI source, schema validation, mocks, CLI for CI, design-first workflow | Two tools to learn, requires spec discipline | Teams that want spec-driven, full-coverage testing |
| Cypress + cy-api plugin | Familiar to Cypress shops | Cypress runs in-browser only; API testing is constrained; plugins less polished | Existing Cypress codebases |
| Pact (consumer-driven contracts) | Strong contract guarantees between services | Steep learning curve, broker infrastructure, not focused on UI | Microservice org with many internal API consumers |
If you’re coming from older SOAP-era tools, our pieces on SoapUI Groovy script alternatives and ReadyAPI alternatives cover the migration path. For local-first workflows, REST client VSCode extensions is worth a read.
The Playwright + Apidog pairing wins for teams that have an OpenAPI spec, ship multiple services, and want a single CI pipeline that catches both UI and API regressions without paying for two SaaS seats per engineer.
Real-world use cases
E-commerce checkout. A retail team runs Playwright tests for the cart-to-confirmation flow and Apidog scenarios for the payment intent, fraud check, and inventory decrement API chain. When the payment gateway switched a response field from error_code to errorCode, Apidog caught it in 90 seconds; Playwright would have shown a generic “checkout failed” screen and taken hours to triage.
SaaS dashboard with chart data. A B2B analytics product validates UI rendering with Playwright snapshots and uses Apidog to assert that aggregation endpoints return correct sums, percentiles, and time-bucketed series. A bug where the p99 latency endpoint silently dropped outliers got caught at the API layer; the chart looked fine.
Webhook-driven workflow. A fintech team uses Playwright for the user-facing portal and Apidog scenarios for webhook delivery, retry logic, and idempotency. Apidog’s scripting verifies that duplicate webhook IDs are rejected, that signatures validate, and that the eventual-consistency window is under 30 seconds.
Conclusion
Playwright is excellent at browser flows. It’s not built for deep API testing. Pairing it with Apidog gives you:
- One OpenAPI spec as the contract between both suites.
- Shared fixtures so test data never drifts.
- Schema-level validation on every endpoint, beyond status codes alone.
- Mock servers for offline development.
- A single CI pipeline that fails on either UI or API regressions.
- Network interception in Playwright, deep scenario chains in Apidog.
- Clear ownership: UI engineers own Playwright specs, API engineers own Apidog scenarios.
Start with one critical journey, like checkout or signup. Wire the Playwright fixture, build the matching Apidog scenario, run both in CI. Expand from there. Download Apidog, import your OpenAPI spec, and you can have the first scenario running today.
FAQ
Can I validate APIs in Playwright tests without Apidog? Yes, using Playwright’s request fixture and manual expect calls. You’ll cover status codes and a few body fields. For schema validation, chained scenarios, mocks, and error-path coverage at scale, a dedicated tool like Apidog is faster and produces fewer false positives. See our API testing tool for QA engineers comparison for the trade-offs.
Do I need an OpenAPI spec to use this setup? You need one to get the full benefit. Without a spec, you can still run Playwright and Apidog side by side, but you lose the shared source of truth and have to maintain example payloads in two places. Generating a spec from existing routes takes a day or two.
How do I handle authentication across both tools? Use a beforeAll step that fetches a fresh token from your auth endpoint, then stash it in a Playwright fixture and an Apidog environment variable. Rotate tokens per test run so stale tokens never cause flakes.
Can Apidog scenarios replace Playwright entirely? No. Apidog is excellent at API workflows but does not render browsers. For UI assertions (visible text, layout, click flows) you still need Playwright. The two tools cover different surfaces.
What if my backend doesn’t have a stable staging environment? Use Apidog’s built-in mock server. It spins up a stateful mock from your OpenAPI spec in one click, returning example responses defined in the spec. Your Playwright suite and Apidog scenarios both run green against the mock, and you switch back to the real backend when staging is healthy.
How do I keep CI fast as the suite grows? Tag tests by priority and run only @smoke on every push. Run the full regression and Apidog scenario suite on PRs to main and on a nightly schedule. Parallelize Playwright with workers: 4 and Apidog scenarios with the CLI’s --parallel flag.
Do I need a paid Apidog plan for CI? The Apidog CLI runs locally and in CI without seat licensing for scenario execution. Check the current pricing page before adopting at scale. The free tier covers most small teams.



