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:- Open
https://www.scrapingcourse.com/javascript-renderingin Chrome. - Right-click the page, choose Inspect, open the Network tab and select the Fetch/XHR filter.
- Reload the page. A
GETtohttps://www.scrapingcourse.com/ajax/products/jsonappears. - Click it and open Response: it is a JSON array with every product’s name, SKU, price and images.
/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
Addjson_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
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)
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 aresponse listener before you navigate. Filter on the resource type and read the body with .json().
pip install playwright or npm install puppeteer-core. Output:
finally block closes the browser even when a step fails, which ends the session and frees its slot.
Customize it
Troubleshooting
- The
xhrlist is empty or missing the call: the capture finished before the call ran. Addwait_forwith a selector of the content the call fills, or awaitof a few seconds. 400with codeREQS004mentioningjson_response: the request has no rendering. Addjs_render=true.json.loadsfails on abody: that call returned HTML, text or an empty body (status204). Filter by URL first, and checkstatus_codebefore parsing.- The Browser listener sees nothing: it was registered after navigation. Call
page.on("response", ...)beforepage.goto. await response.json()fails in Browser: the response was a redirect or had no body. Checkresponse.statusand read the body of200responses.
Related recipes
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.