REST API Playground for Testing Browser Automation
Use Playwright requests, route mocks, or Postman servers to control API behavior in browser tests and verify the right layer.
A REST API playground helps browser-automation tests prepare server state, inspect outcomes, or supply controlled responses. The right workflow depends on where you need control:
- Use Playwright APIRequestContext to create data before a page flow or verify backend state afterward.
- Use Playwright routing or HAR files when the page must receive deterministic responses without calling the live service.
- Use a Postman mock server when several clients need a hosted endpoint backed by saved request examples.
These workflows are related, but they establish different facts. A direct request exercises an API endpoint. An intercepted response proves how the page behaves for the supplied payload. A hosted mock serves examples to clients outside one test process.
What a REST API playground contributes
In browser automation, a playground is a controlled place to send HTTP requests, inspect responses, and decide whether the browser talks to a real or simulated backend. It can be a request tool inside your test framework, a network interceptor at the page boundary, or a hosted mock service.
Keep the test question explicit:
| Question | Best fit | What it establishes |
|---|---|---|
| Can I create or inspect backend state around a UI flow? | Playwright APIRequestContext |
The test issued API requests for setup or postconditions. |
| Does the page render a known payload correctly? | Playwright page.route or HAR mocking |
The page received the response supplied by the test. |
| Can several clients reuse endpoint examples? | Postman mock server | A hosted endpoint served saved collection examples. |
| Can I run API assertions independently of the UI? | Postman collection scripts or direct API tests | Requests and assertions ran as an API workflow. |
Playwright documents direct HTTP(S) requests through APIRequestContext, browser test setup in its API testing guide, and interception in Mock APIs. Postman documents scripted collections at Test APIs and write scripts and hosted mocks at its mock server overview.
Workflow 1: direct API calls inside a Playwright test
Use direct requests when the browser test needs authenticated setup, a fixture that is awkward to create through the UI, or a server-side assertion after a visible action.
Choose the request context
A request object created from a browser context shares that context’s cookie jar. An isolated request context has separate cookie storage. Choose the shared form when the browser login session should authenticate the API call; choose an isolated context when setup credentials and browser credentials must remain separate.
Runnable TypeScript example
import { test, expect } from '@playwright/test';
test('updates a profile and verifies the API state', async ({ page, context }) => {
const api = await context.request.newContext({
baseURL: process.env.API_BASE_URL,
extraHTTPHeaders: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
Accept: 'application/json'
}
});
let userId: string | undefined;
try {
const create = await api.post('/test-users', {
data: { name: 'Browser test user', plan: 'trial' }
});
expect(create.ok()).toBeTruthy();
userId = (await create.json()).id;
await page.goto(`${process.env.APP_URL}/profile/${userId}`);
await page.getByLabel('Display name').fill('Updated name');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Profile saved')).toBeVisible();
const check = await api.get(`/test-users/${userId}`);
expect(check.ok()).toBeTruthy();
await expect.poll(async () => (await check.json()).name).toBe('Updated name');
} finally {
if (userId) {
const remove = await api.delete(`/test-users/${userId}`);
expect([200, 202, 204, 404]).toContain(remove.status());
}
await api.dispose();
}
});
Use a dedicated test environment and supply credentials through your CI secret store. Do not hard-code tokens. Cleanup belongs in a finally block so a failed assertion does not leave data behind.
When this is faster and when it is not
Creating state through an API can be more targeted than clicking through every setup screen. That is a workflow implication of Playwright’s documented request capability, not a benchmark. The request still depends on a reachable service, valid test data, authentication, and compatible API behavior.
Workflow 2: intercept and mock requests at the browser boundary
Use routing when the page should receive a known response regardless of the live backend. Playwright can intercept HTTP and HTTPS traffic, including XHR and fetch, then continue, modify, abort, or fulfill a request. HAR files provide a repeatable set of recorded interactions.
Fulfill a JSON response
import { test, expect } from '@playwright/test';
test('renders an empty projects response', async ({ page }) => {
await page.route('**/api/projects**', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ projects: [], total: 0 })
});
});
await page.goto('https://app.example.test/projects');
await expect(page.getByText('No projects yet')).toBeVisible();
});
Modify only part of a live response
await page.route('**/api/cart', async route => {
const response = await route.fetch();
const json = await response.json();
json.items = json.items.map((item: any) => ({ ...item, price: 0 }));
await route.fulfill({ response, json });
});
A fulfilled response tests the page’s behavior against the payload you supplied. It does not prove that the live backend returns that payload. Keep a separate live API test for backend correctness.
Use HAR recording and replay
import { test } from '@playwright/test';
test('replays recorded network traffic', async ({ page }) => {
await page.routeFromHAR('./fixtures/catalog.har', {
url: '**/api/**',
update: false,
notFound: 'abort'
});
await page.goto('https://app.example.test/catalog');
});
Record a HAR against a controlled environment, review it for secrets, and commit only sanitized fixtures. An aborted unmatched request is useful because it exposes accidental dependencies instead of silently calling production.
Workflow 3: build a hosted mock with Postman
Postman collections let you save requests and examples, add scripts that assert responses, and run the collection manually or through automation. A Postman mock server exposes saved examples so an application or test client can call a hosted endpoint.
- Create an HTTP collection containing the endpoint requests your client needs.
- Send each request and save one or more representative examples, including status, headers, and body.
- Create a mock server from the collection. The mock selects a saved example based on the incoming request.
- Point the browser application’s configurable API base URL at the mock host.
- Run the same client tests against success, empty, validation-error, and server-error examples.
Dynamic responses are available when configured, but do not promise arbitrary behavior without defining examples and matching rules. Treat public and private access separately; Postman’s tutorial states that private mocks require an API key.
Example collection test script
pm.test('returns a project list', function () {
pm.response.to.have.status(200);
const body = pm.response.json();
pm.expect(body).to.have.property('projects');
pm.expect(body.projects).to.be.an('array');
});
A hosted mock is useful when a mobile client, browser app, contract test, and manual tester all need the same examples. It adds setup and access-control work compared with an in-test route handler.
How to choose the workflow
| Need | Use | Check before adopting |
|---|---|---|
| Sign in through UI, then inspect server state | Context request sharing browser cookies | Cookie scope, CSRF requirements, cleanup |
| Seed data with service credentials | Isolated API request context | Separate authentication and environment safety |
| Render a deterministic success or error state | page.route or HAR |
Mock coverage and an independent live API test |
| Share examples across applications | Postman hosted mock | Example matching, API keys, public/private exposure |
| Test the API without a browser | Postman collection scripts or direct requests | Assertions, retries, data isolation |
Edge cases and test design
- Authentication: decide whether cookies, bearer tokens, client certificates, or custom headers belong to the browser context, API context, or mock client.
- CSRF: a direct API call may need the same CSRF token and cookie pair that a page obtains during navigation.
- Eventual consistency: poll a read endpoint with a bounded timeout after a mutation instead of assuming immediate visibility.
- Pagination: save examples for first page, middle page, empty page, and an invalid cursor.
- Retries: avoid retrying non-idempotent setup blindly. Use unique test identifiers and server-side idempotency keys where supported.
- Time and locale: fix timezone and locale in test environments so date formatting does not change snapshots.
- WebSockets and service workers: HTTP route mocks do not automatically model every non-HTTP channel. Test those paths with the protocol-specific tooling they require.
- Parallel workers: namespace created records by worker and run identifier, then delete only records owned by that test.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| API setup returns 401 | Token is missing, expired, or sent to the wrong host. | Log the method, URL, and status without logging secrets; verify CI variables and environment base URL. |
| Browser route never matches | Pattern does not include the actual path, query, or origin. | Temporarily log requests, then use a precise glob or predicate covering the full URL. |
| Page shows stale data after setup | Read-after-write delay or cached response. | Poll with a deadline, invalidate the application cache, or use unique data. |
| HAR replay aborts unexpectedly | An unrecorded request is required by the page. | Record the missing dependency, sanitize it, and update the HAR deliberately. |
| Mock client receives 404 | No saved Postman example matches method, path, or headers. | Save an example for the exact request and confirm the mock’s matching rules. |
| Tests pass with mocks but fail against staging | The mock hides backend schema, auth, or timing differences. | Run a separate live contract or integration suite and compare response schemas. |
| Cleanup fails after a test error | Cleanup is not in a finally hook or uses a different credential. | Move deletion to teardown/finally and make cleanup idempotent. |
Performance, reliability, and cost
Direct API setup reduces UI actions, which can shorten a test’s path, but network latency and backend load remain. Route and HAR mocks remove service variability and are usually more repeatable, while sacrificing evidence about the live backend. Hosted mocks centralize examples but add a network dependency and access-control configuration.
For reliable suites:
- Keep fast mocked component flows and a smaller set of live end-to-end flows.
- Pin response fixtures and review changes as API contract changes.
- Set explicit request, navigation, and polling timeouts.
- Capture request IDs and response status in CI logs while redacting credentials.
- Use disposable test data and deterministic clocks where possible.
Cost is driven by the service you call, CI minutes, and hosted mock usage. Avoid sending load tests through a browser test suite; use an API load-testing tool and an environment designed for it.
Or skip the browser setup
If the goal is to capture a page image or PDF during an automation workflow, ScreenshotNeo provides a single screenshot API request. Its capture pipeline accepts cookie and consent banners, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets each cleanup step be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and whether the shot was billed.
See the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const file = await res.arrayBuffer();
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(file)));
The API also supports full-page shots with lazy images loaded, CSS-element capture, dark mode, device presets, custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, blocked resources, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots. Yearly billing gives two months free, and every feature is on every plan. Create a free ScreenshotNeo account.
FAQ
Does a Playwright mock test my backend?
No. It tests the page against the response you supplied. Pair it with a live API or contract test for backend behavior.
Should API setup share browser cookies?
Share the browser context when the API call must use the browser’s session. Use an isolated request context for independent setup credentials.
When is Postman preferable to Playwright routing?
Use Postman when saved examples must be hosted and reused by multiple clients. Use routing when control belongs inside one browser test.
Can one suite use all three workflows?
Yes. A common design seeds data through an API, mocks selected page responses for deterministic UI cases, and runs a smaller live suite against the real service.
How should secrets be handled?
Inject them from CI or a secret manager, redact logs, and use a dedicated test environment. Never commit tokens in code or HAR files.


