Monitor and inspect HTTP network requests made by a web page via Playwright MCP
A common agent task: you need to understand what API calls a web page makes — for reverse-engineering, debugging, or verification. The Playwright MCP server exposes browser_network_requests to list all requests since page load (filterable by URL pattern), and browser_network_request to inspect any individual request's headers, response body, timing, and MIME type. Combined with browser_evaluate to trigger fetch() calls, this gives agents full HTTP-level observability over any web page.
Recipe: Monitor & inspect HTTP network requests via Playwright MCP
Surface: @playwright/mcp (npx @playwright/mcp) Transport: stdio Auth: none
How it works
The Playwright MCP server automatically records every HTTP request the browser makes. Three tools form the network monitoring pipeline:
- `browser_navigate` — load the target page (requests start recording)
- `browser_evaluate` — optionally trigger fetch() calls to generate API traffic
- `browser_network_requests` — list all requests with URL, method, and status (filter by URL regex, toggle static resources)
- `browser_network_request` — drill into any request by index for full headers, timing, response body
Step-by-step trace
Step 1: Navigate to the target page
→ browser_navigate({ url: "https://jsonplaceholder.typicode.com" })
← Page loaded: "JSONPlaceholder - Free Fake REST API"Step 2: Trigger API calls via JavaScript evaluation
→ browser_evaluate({ function: "async () => { ... fetch('/posts?_limit=2'), fetch('/users/1'), fetch('/posts/1/comments?_limit=2') ... }" })
← { posts_count: 2, user_name: "Leanne Graham", comments_count: 2 }Step 3: List network requests (filtered to API calls)
→ browser_network_requests({ static: false, filter: "/api|/posts|/users|/comments" })
← 21. [GET] https://jsonplaceholder.typicode.com/posts?_limit=2 => [200]
22. [GET] https://jsonplaceholder.typicode.com/users/1 => [200]
23. [GET] https://jsonplaceholder.typicode.com/posts/1/comments?_limit=2 => [200]
Note: 17 static requests not shownStep 4: Inspect a specific request's full details
→ browser_network_request({ index: 22 })
← #22 [GET] https://jsonplaceholder.typicode.com/users/1
status: [200], duration: 80ms, type: fetch, mimeType: application/json
Request headers: sec-ch-ua-platform: "macOS", user-agent: Mozilla/5.0 ...
Response headers: cf-cache-status: HIT, content-type: application/json; charset=utf-8,
x-ratelimit-remaining: 999, x-ratelimit-limit: 1000, server: cloudflare, ...Step 5: Read the response body
→ browser_network_request({ index: 22, part: "response-body" })
← { "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]",
"address": { "street": "Kulas Light", "city": "Gwenborough", ... },
"company": { "name": "Romaguera-Crona", ... } }Key parameters
| Tool | Parameter | Purpose |
|---|---|---|
browser_network_requests | static: false | Hide images/fonts/scripts — show only fetch/XHR/document |
browser_network_requests | filter: "/api/.*" | Regex to narrow by URL pattern |
browser_network_request | index: N | 1-based request number from the list |
browser_network_request | part: "response-body" | Get just the response body (also: request-headers, request-body, response-headers) |
Use cases
- API reverse-engineering: discover undocumented endpoints a SPA calls
- Debugging: verify an API returns expected data/headers
- Security auditing: check for leaked tokens in request headers
- Performance: measure request durations and identify slow calls
- Testing: verify a frontend correctly calls backend endpoints after user actions
{ "surface": "@playwright/mcp", "transport": "stdio", "launcher": "npx @playwright/mcp", "tools_used": ["browser_navigate", "browser_evaluate", "browser_network_requests", "browser_network_request"], "trace": { "step_1_navigate": { "tool": "browser_navigate", "input": { "url": "https://jsonplaceholder.typicode.com" }, "output": "Page URL: https://jsonplaceholder.typicode.com/, Page Title: JSONPlaceholder - Free Fake REST API" }, "step_2_trigger_api_calls": { "tool": "browser_evaluate", "input": { "function": "async () => { const [posts, user, comments] = await Promise.all([fetch('/posts?_limit=2').then(r => r.json()), fetch('/users/1').then(r => r.json()), fetch('/posts/1/comments?_limit=2').then(r => r.json())]); return { posts_count: posts.length, user_name: user.name, comments_count: comments.length }; }" }, "output": { "posts_count": 2, "user_name": "Leanne Graham", "comments_count": 2 } }, "step_3_list_requests": { "tool": "browser_network_requests", "input": { "static": false, "filter": "/api|/posts|/users|/comments" }, "output": "21. [GET] .../posts?_limit=2 => [200] 22. [GET] .../users/1 => [200] 23. [GET] .../posts/1/comments?_limit=2 => [200]" }, "step_4_inspect_request": { "tool": "browser_network_request", "input": { "index": 22 }, "output_summary": "status: 200, duration: 80ms, type: fetch, mimeType: application/json, cf-cache-status: HIT, server: cloudflare" }, "step_5_response_body": { "tool": "browser_network_request", "input": { "index": 22, "part": "response-body" }, "output": { "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]" } } }, "executed_at": "2026-06-11T00:20:19Z", "total_duration_ms": 3500 }