Skip to main content
Many pages load their data through background XHR and fetch calls, and those calls often return clean JSON. Capture them and you get structured data without parsing any HTML. Fetch returns every call in one response; Browser lets you listen to them live from Playwright or Puppeteer. Uses: Fetch with json_response, js_render and wait_for; Browser with Playwright or Puppeteer

Ask your AI agent

With the Zenrows MCP server connected to Claude, Cursor or any MCP client, paste this prompt. Your agent picks the right Zenrows tools and settings on its own.

Find the request that carries the data

Before you capture anything, find the call in your own browser:
  1. Open https://www.scrapingcourse.com/javascript-rendering in Chrome.
  2. Right-click the page, choose Inspect, open the Network tab and select the Fetch/XHR filter.
  3. Reload the page. A GET to https://www.scrapingcourse.com/ajax/products/json appears.
  4. Click it and open Response: it is a JSON array with every product’s name, SKU, price and images.
The URL fragment you spotted, here /ajax/products, is what you filter on in the code below. Calls triggered by an action, such as a “Load more” click, show up when you perform that action with DevTools open.

Capture requests with Fetch

Add json_response=true to a rendered request. The response becomes JSON with the page html and an xhr list: one entry per background call, with its url, method, status_code, headers and body. wait_for holds the capture until the data has arrived.

Example output

Each body is a string: parse it with json.loads or JSON.parse when the call returns JSON.

Call the endpoint directly

Once you know the endpoint, you can often request it straight away. It skips rendering, so it is faster and cheaper than capturing it from the page.
Python (Requests)
Output: 12 products, 1 credit. When the endpoint needs the page’s cookies or tokens, keep capturing it from the page instead.

Capture requests with Browser

For multi-step flows, or when you want each response as it happens, connect Playwright or Puppeteer to Zenrows Browser and register a response listener before you navigate. Filter on the resource type and read the body with .json().
Install the client first: pip install playwright or npm install puppeteer-core. Output:
The finally block closes the browser even when a step fails, which ends the session and frees its slot.

Customize it

Troubleshooting

  • The xhr list is empty or missing the call: the capture finished before the call ran. Add wait_for with a selector of the content the call fills, or a wait of a few seconds.
  • 400 with code REQS004 mentioning json_response: the request has no rendering. Add js_render=true.
  • json.loads fails on a body: that call returned HTML, text or an empty body (status 204). Filter by URL first, and check status_code before parsing.
  • The Browser listener sees nothing: it was registered after navigation. Call page.on("response", ...) before page.goto.
  • await response.json() fails in Browser: the response was a redirect or had no body. Check response.status and read the body of 200 responses.

Scrape JavaScript-rendered pages

Render the page and wait for the content you need.

Connect Playwright or Puppeteer to Zenrows Browser

Drive Zenrows Browser from your existing scripts.

Click, fill, and scroll before you scrape

Trigger the calls you want to capture.

Get structured JSON from any page

Get product, listing or article fields as JSON.