How to Pull Live Web Data into Claude, Cursor, and Any AI Agent with Zenrows MCP
Learn how to connect Zenrows MCP to Claude Desktop, Cursor, and other AI agents to fetch live web data, render JavaScript, and scrape protected websites.
Most AI assistants can't reliably access live web pages the way a browser can. They search the web, which sounds similar but isn't. Search returns snippets from a cached index. Ask Claude to check a competitor's live pricing page, pull data from a JavaScript-rendered listing, or access a protected website, and you'll get stale data at best, nothing at worst.
The Zenrows MCP server closes that gap. You connect it to Claude Desktop, Cursor, or any other MCP-compatible client, and your assistant can fetch live URLs, render JavaScript, and access protected pages with a 99.93% success rate against anti-bot systems.
This tutorial covers setup for Claude Desktop and Cursor, then shows the same connection pattern for other MCP-compatible clients and a custom application.
Skip the blocks. Try Zenrows free and get clean web data without the anti-bot fight.
What the Zenrows MCP Server Does
The Zenrows MCP server exposes one request-based tool and one family of browser tools to your AI assistant.
The first is scrape. You give it a URL, and it returns the page content in whatever format you need: Markdown by default, but also plain text, HTML, PDF, screenshot, or structured JSON. It handles JavaScript rendering and access to protected websites in a single request. Use it when you need data from a page and don't need to interact with it.
The second is the browser_* family. This one gives your assistant control of a cloud-hosted browser session. Use them when the task requires clicking, form filling, multi-step navigation, or waiting for dynamically loaded content.
| Tool family | What it does | When to use it |
|---|---|---|
scrape |
Fetches a URL and returns clean content in your chosen format. | Static pages, JavaScript-rendered pages, and protected pages where you only need to extract data. |
browser_* |
Controls a cloud-hosted browser session with full interaction support. | Multi-step workflows that require clicks, form fills, scrolling, or navigation across pages. |

The browser_* tools come with built-in residential proxies and access to protected websites. The scrape tool also supports Premium Proxies and protected web access. Your assistant requests them automatically when the target page needs them.
The same server is available as a local subprocess for desktop clients like Claude Desktop and Cursor, and as a remote HTTP endpoint for custom applications that call an LLM API directly. This means you can start with a local setup for desktop assistants or use the HTTP endpoint to call Zenrows MCP from your own application.
Setting up Zenrows MCP in Claude Desktop
Before you configure Claude Desktop, make sure you have these prerequisites:
- A Zenrows account and your API key from the Zenrows dashboard.
- Node.js installed on your machine. The local setup runs via
npx, so Node.js is required. Runnode --versionin your terminal to confirm. - Claude Desktop installed.
Step 1: Open the configuration file
On macOS, the file is at:
Terminal
~/Library/Application Support/Claude/claude_desktop_config.json
Copied!
On Windows, press Windows + R, paste the following into the Run dialog, and hit Enter:
Terminal
%APPDATA%\Claude
Copied!
This opens the Claude folder in File Explorer. Open claude_desktop_config.json in a text editor. If the file doesn't exist, create it. If the file already has content, add the mcpServers block alongside the existing keys without replacing anything.
Step 2: Add the Zenrows config block
Paste this MCP config into claude_desktop_config.json, alongside any existing keys. Replace YOUR_ZENROWS_API_KEY with your own key before saving:
claude_desktop_config.json
{
"mcpServers": {
"zenrows": {
"command": "npx",
"args": ["-y", "@zenrows/mcp"],
"env": {
"ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY"
}
}
}
}
Copied!
Step 3: Restart Claude Desktop
Save the file, fully quit Claude Desktop, and reopen it. On Windows, closing the window isn't enough. Right-click the Claude icon in the system tray and select Quit.
Once the server restarts, open the connectors menu to confirm Zenrows is active.

Setting up Zenrows MCP in Cursor
Cursor uses the same prerequisites as Claude Desktop: a Zenrows account, your API key, Node.js, and Cursor installed on your machine.
Cursor's setup uses the same JSON block as Claude Desktop. Only the file path is different. Open or create the MCP configuration file at:
- Global (all projects): ~/.cursor/mcp.json
- Project-specific: .cursor/mcp.json in your project root
On Windows, you can navigate to the global config folder by pressing Windows + R and running:
Terminal
%USERPROFILE%\.cursor
Copied!
Paste the following into the file, replacing YOUR_ZENROWS_API_KEY with your actual key:
claude_desktop_config.json
{
"mcpServers": {
"zenrows": {
"command": "npx",
"args": ["-y", "@zenrows/mcp"],
"env": {
"ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY"
}
}
}
}
Copied!
Save the file and restart Cursor. Open the MCP Servers menu to confirm Zenrows is active.

Note: Windsurf uses the same JSON format and file path as Cursor.
With Zenrows connected, here's what your assistant can do.
What Your Assistant Can Do Once Connected
Once Zenrows MCP is connected, your assistant can fetch JavaScript-rendered pages, access protected pages, and extract search results.
Fetching a JavaScript-rendered page
The page at https://www.scrapingcourse.com/javascript-rendering/ loads its product data client-side via an AJAX request. Fetching the raw HTML returns empty placeholders. With JavaScript rendering enabled, Zenrows waits for the AJAX request to finish before returning the page. That's what lets your assistant see the 12 rendered products instead of empty placeholders. Type this prompt in:
Use the Zenrows scrape tool to fetch https://www.scrapingcourse.com/javascript-rendering/ with JavaScript rendering enabled and return the page content.
The assistant returned all 12 products, along with their names and prices.

Accessing protected pages
Some pages reject automated requests entirely. Type this prompt into Claude Desktop or Cursor to fetch a protected page:
Use the Zenrows scrape tool to fetch https://www.scrapingcourse.com/antibot-challenge and extract the page content.
Zenrows returned a REQS002 error on the first request, signaling that the page requires both JavaScript rendering and Premium Proxies. The assistant retried with both parameters enabled and returned the page content.

Extracting search results
You can also use Zenrows to extract data from search results pages. Pass the query as a URL parameter and describe what you need. If the data requires visiting individual product pages, your assistant handles that automatically. Type this into Claude Desktop or Cursor:
Use the Zenrows scrape tool to fetch https://www.scrapingcourse.com/ecommerce/?s=hoodie and extract all matching product names and prices.
The assistant identified that prices are only available on individual product pages, fetched each of the 10 product pages separately, and returned a table with names and prices for all matching products.

Using Zenrows MCP in a Custom Application
Any application that calls an LLM API directly can connect to the Zenrows MCP server as a remote HTTP endpoint, without a local installation. The remote server runs at https://mcp.zenrows.com/mcp and accepts your API key as a Bearer token.
The app below tracks Hacker News headlines and reports what has changed between runs. It fetches the current top 10 stories, saves them as a baseline on the first run, and compares against that baseline on every subsequent run.
Install the OpenAI Python SDK before starting:
Terminal
pip install openai
Copied!
Set your Zenrows and OpenAI API keys as environment variables, then set up the client and point it at the Zenrows MCP server:
File
import json
import os
from openai import OpenAI
ZENROWS_API_KEY = os.environ["ZENROWS_API_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
SNAPSHOT_FILE = "hn_snapshot.json"
TARGET_URL = "https://news.ycombinator.com"
client = OpenAI(api_key=OPENAI_API_KEY)
def fetch_headlines():
response = client.responses.create(
model="gpt-5",
tools=[
{
"type": "mcp",
"server_label": "zenrows",
"server_description": "Web scraping MCP server for accessing live web content.",
"server_url": "https://mcp.zenrows.com/mcp",
"headers": {
"Authorization": f"Bearer {ZENROWS_API_KEY}"
},
"require_approval": "never",
}
],
input=f"Use the ZenRows scrape tool to fetch {TARGET_URL} and extract the top 10 story titles and their points as numbers only. Return only a JSON object where keys are story titles and values are point counts as plain numbers with no extra text.",
)
return response.output_text
Copied!
The headers field passes your Zenrows API key as a Bearer token on every request. Setting require_approval to "never" allows the model to call the Zenrows tools without waiting for confirmation on each request.
Next, add the snapshot and change detection logic. load_snapshot reads the previously saved data, save_snapshot writes the latest fetch to disk, parse_response extracts the JSON from the model's output and handles cases where the model adds surrounding text to the response, and compare identifies what's new, what dropped off, and what changed:
File
def load_snapshot():
if os.path.exists(SNAPSHOT_FILE):
with open(SNAPSHOT_FILE, "r") as f:
return json.load(f)
return None
def save_snapshot(data):
with open(SNAPSHOT_FILE, "w") as f:
json.dump(data, f, indent=2)
def parse_response(text):
try:
start = text.index("{")
end = text.rindex("}") + 1
return json.loads(text[start:end])
except Exception:
print("Could not parse response as JSON.")
print(text)
return None
def compare(old, new):
added = {k: v for k, v in new.items() if k not in old}
removed = {k: v for k, v in old.items() if k not in new}
changed = {k: v for k, v in new.items() if k in old and old[k] != v}
return added, removed, changed
Copied!
The app checks for an existing snapshot, runs the comparison if one exists, and saves the latest data either way:
File
print(f"Fetching top stories from {TARGET_URL}...\n")
raw = fetch_headlines()
current = parse_response(raw)
if current is None:
exit(1)
previous = load_snapshot()
if previous is None:
print("No previous snapshot found. Saving baseline.\n")
save_snapshot(current)
print("Top stories:")
for title, points in current.items():
print(f" - {title} ({points} points)")
else:
added, removed, changed = compare(previous, current)
save_snapshot(current)
print("Change report:\n")
if not added and not removed and not changed:
print(" No changes detected.")
if added:
print(" New stories:")
for k, v in added.items():
print(f" + {k} ({v} points)")
if removed:
print("\n Dropped off:")
for k in removed:
print(f" - {k}")
if changed:
print("\n Point changes:")
for k, v in changed.items():
print(f" ~ {k}: now {v} points")
Copied!
The first run saves the baseline and lists the current top stories:

Run it again, and the tracker reports what changed since the last snapshot:

This is a change detection workflow. The same pattern applies to any monitoring task where you need to track what's different between two points in time, from competitor pricing to documentation updates.
Note: The same pattern works with the Anthropic SDK. Pass your Zenrows API key through the authorization field on the MCP tool config.
Wrapping Up
This tutorial walked you through connecting Zenrows MCP to Claude Desktop and Cursor, fetching JavaScript-rendered and protected pages from a prompt, and building a Python application that directly calls the Zenrows MCP server.
Now you know:
- How to add the Zenrows MCP server to Claude Desktop and Cursor with a single config block
- How to fetch JavaScript-rendered and bot-protected pages directly from a prompt
- How to call the remote Zenrows MCP server programmatically from a custom application
Zenrows MCP connects once and stays available for every scraping task your assistant runs. JavaScript rendering, protected web access, and proxy rotation are handled on Zenrows' side.
Try Zenrows for free and see what your assistant can reach.