Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

io.github.ZenRows/zenrows-mcp

NPM · @ZENROWS/MCP · 2 COMPONENTS

Zenrows MCP server: Fetch, Extract, Batch, and Browser Sessions for AI agents

+4 this week 84 Trust /100

Last scanned (23 hours ago)

Scores higher than 81% of npm servers

Category Web Scraping

Trust breakdown (7 categories)

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →

Supply Chain Security81
  • No malware found by supply-chain analysis.Pass
  • CVE check failed: a known high-severity CVE affects zod 3.25.76, a direct dependency. A fixed version is available. View diagnostics → Fail
  • No install/post-install scripts declared.Pass
  • 30 of 94 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
  • Source repository is publicly reachable at the declared URL. View diagnostics → Pass
  • Cryptographically verified build provenance (signed, bound to ZenRows/zenrows-mcp). View diagnostics → Pass
  • Clear OSI-approved license (MIT).Pass
  • Actively maintained (last published 4 days ago).Pass
  • Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability79
  • 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
  • AI-judged instruction clarity (good).Pass
  • Context-footprint check failed: tool/resource definitions use about 10090 tokens (~157/item across 64 items; 63 tools + 1 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management55
  • Stability check failed: the tool surface changed between 2.12.0 and 2.18.0: 1 tool removals, 0 breaking changes, 12 additions. See how to fix → Fail
Tool Coverage100
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 100% of tool parameters carry a description.Pass
Tool Safety100
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • All 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
  • An AI judge read all 65 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 4 Nov 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 51 to 55.

  • 2 Nov 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 45 to 48.

  • 31 Oct 26 +16
    • Malware scan: unverified → pass ▲ security
  • 30 Oct 26 −15
    • Malware scan: pass → unverified ▼ security
    • Package version: 2.17.0 → 2.18.0 functional
  • 29 Oct 26 +1
    • CVE-2026-118663 affects this package: high ▼ security
  • 28 Oct 26 +15
    • Malware scan: unverified → pass ▲ security
  • 27 Oct 26 −14
    • Malware scan: pass → unverified ▼ security
    • Package version: 2.16.1 → 2.17.0 functional
  • 25 Oct 26 +21
    • CVE-2023-54404 affects this package: high ▼ security
    • Known CVEs: unverified → fail ▼ security
    • Malware scan: unverified → pass ▲ security
    • Dependency health: unverified → 0.86 ▲ functional
Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 4 Nov 2026 · Analysed npm/@zenrows/mcp@2.18.0

Provenance Verified

A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.

Result Verified
Ecosystem npm
Reason Verified
Discovered via Registry attestation endpoint
Source repo ZenRows/zenrows-mcp
Certificate issuer https://token.actions.githubusercontent.com
Certificate SAN https://github.com/ZenRows/zenrows-mcp/.github/workflows/deploy.yml@refs/heads/main
Rekor log index 3592171529
Predicate type SLSA build provenance https://slsa.dev/provenance/v1
Subject digest sha512:35104a47b682c0735ea26306b7f2b1de4fba0378477a6f3f6a8299d2235f1ec80e2ec6f5bbad50933f8f966e5610da2888909631fb690c9b643c2c61f

Background: How many MCP packages publish verified provenance →

Vulnerabilities 2 findings
ID CVE Severity Vector Fix available
GHSA-rmxm-3fg6-px4f CVE-2026-118663 medium CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N yes
GHSA-mrhx-c7xc-m3w5 CVE-2023-54404 high CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H yes

Background: What a vulnerability scan can and cannot prove →

Dependencies 94 packages
Packages resolved 94
Stale 30
Tree resolution Complete

Background: SBOMs and build attestations, explained →

MCP tools · 63 exposed · ~9,933 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
browser_wait_for_selector ~91

Wait until an element matching the CSS selector is stable in the DOM. Set visible=true to also require the element to be visible (not hidden).

NameTypeReqDescription
selectorstringyesCSS selector to wait for
session_idstringyesSession ID returned by browser_navigate
visibleboolean|null–Also require the element to be visible, not just present in the DOM (default false)

No output schema declared.

No examples provided.

crawl_content ~192

Read the stored page of one URL a crawl kept, in the crawl's output_format. Works only for crawls created with an output_format, and only for results whose content_status is fetched. Pass crawl_id and content_id: a content id, or content_url from a crawl_results row. Returns the page as stored — raw HTML for output_format "html", Markdown for "markdown" — cut to max_chars (default 20000); a final note says when it was cut. To read a page the crawl did not store, use scrape on its URL instead.

NameTypeReqDescription
content_idstringyesA content id (ct_…), or content_url from a crawl_results row; its last path segment is the id
crawl_idstringyesCrawl id returned by crawl_create (c_…)
max_chars––Max characters of the page to return (default 20000)

No output schema declared.

No examples provided.

crawl_create ~781

Start a crawl from one URL (Zenrows Crawl API). Crawl follows the links in each page's HTML up to depth hops (no depth limit when depth is omitted), keeps the URLs that match include_patterns / exclude_patterns, and stays on the start URL's registrable domain (subdomains count). Use crawl when you have one start page (a listing, category, blog index) and need the URLs behind it, optionally with each page's HTML. For a single page, use scrape. Each page fetched is billed as one scrape on this account; max_pages bounds the cost (default 100), max_items bounds how many URLs are kept (default 100). Without depth, the crawl keeps following links until max_pages or max_items stops it or nothing is left to open, so set max_pages to what you are willing to spend. The start page itself is never a result, but its fetch is billed: a crawl whose include_patterns / exclude_patterns match nothing still costs that one scrape. A crawl whose pages held nothing new to follow (no link, only links to other sites, or only links the patterns reject) ends completed with no results and no error. When patterns are set, check them against the URLs the start page links to. Set output_format "html" or "markdown" to also store each kept URL's page, then read it with crawl_content. Set webhook to have signed events posted to your https URL when the crawl starts and when it ends; the account needs a webhook signing key, else CRAWL_INVALID_REQUEST (webhook_signing_key_required). Returns crawl_id, status and the crawl (with coverage) at once; the crawl runs asynchronously. Call crawl_wait, then crawl_results. Set idempotency_key to make a retry safe: the same key returns the crawl the first request created instead of starting another. CRAWL_NOT_ENABLED: Crawl is not enabled for this account; do not retry. CRAWL_TOO_MANY_CRAWLS: the account has reached its limit of active jobs (3 by default), shared with its Batch jobs; retry after retry_after seconds.

NameTypeReqDescription
depth––Link hops to follow from the start URL. 1 returns the start page's links; 2 also opens those. Omit for no depth limit: the crawl follows links until max_pages or max_items stops it.
discovery––How the crawl finds the URLs it follows on each page it walks. ['links'] follows the links in each page's HTML, and is what a crawl runs with when you omit this.
exclude_patterns––Drop URLs containing any of these substrings, even if they match include_patterns
idempotency_key––Sent as the Idempotency-Key header. Reusing a key returns the crawl it created; reusing it with a different body is CRAWL_INVALID_REQUEST.
include_patterns––Keep only URLs containing at least one of these substrings, e.g. ["/product/"]
max_items––Stop once this many URLs are kept (default 100)
max_pages––Stop once this many pages are fetched, each billed as one scrape (default 100). With output_format, every kept URL's page counts too.
output_format––'html' or 'markdown' also stores each kept URL's page for crawl_content, as fetched or converted to Markdown. Omit for URLs only.
urlstringyesStart URL: a public http(s) page, such as a listing or category page
webhook––Where to post the crawl's signed events (crawl.started, then one of crawl.completed, crawl.stopped, crawl.failed). Each is signed with the account's webhook signing key, in the X-Signature header.

No output schema declared.

No examples provided.

crawl_download ~270

Save every result of a crawl to one local file, as JSON lines: url, content_status and, once fetched, content (the page). Returns the file's path and line count, not the results, so a crawl of any size costs one small reply. Read the file with your own tools. The file goes to path when given (a relative path inside ~/.zenrows/crawls; an absolute path or one that leaves that folder fails with INVALID_PATH), else ~/.zenrows/crawls/<crawl_id>.jsonl. An existing file is kept and the call fails with FILE_EXISTS unless overwrite is true. On a running crawl the file holds only what was kept so far: download again once crawl_wait says it ended. After crawl_stop, pages in flight can still add lines for up to 10 minutes: the reply says so with partial: true. On the hosted server no file is written: the reply gives the download URL and a command to run where your API key is set.

NameTypeReqDescription
crawl_idstringyesCrawl id returned by crawl_create (c_…)
overwriteboolean|null–Replace the file if it exists (default false)
path––File to write, relative to ~/.zenrows/crawls (default <crawl_id>.jsonl)

No output schema declared.

No examples provided.

crawl_get ~384

Get a crawl and one page of the URLs it kept: crawl_id, status (running, completed, stopped, failed), the crawl (with coverage: pages_fetched, pages_failed, items_found; webhook when set), results and next_cursor. completed with stop_reason max_items / max_pages means a limit ended it; stopped with stop_reason user means crawl_stop ended it; stopped with no stop_reason and error.code insufficient_credits or api_key_cap_reached means the account ran out of credits or the API key hit a credit cap (the URLs kept before that stay readable); completed with no results and no error means the crawl's pages held nothing new to follow; when include_patterns / exclude_patterns are set, check them against the URLs the start page links to. failed carries error.code and error.detail: seed_unreachable means the start URL could not be fetched or returned an empty page; no_items_found means the crawl read the start page but found no data it could use, so it had nothing to follow; start from a listing or product page. A failed crawl, and a stopped one with an error, answer ok: false: error.code is CRAWL_INSUFFICIENT_CREDITS or CRAWL_KEY_CAP_REACHED (the codes crawl_create gets on a 402) when credits ended it, failed or stopped, else CRAWL_FAILED; error.server_code is the crawl's error.code. Pass next_cursor as cursor for the next page; while the crawl runs, next_cursor is never null. To read many pages at once, use crawl_results.

NameTypeReqDescription
crawl_idstringyesCrawl id returned by crawl_create (c_…)
cursorstring|null–next_cursor from a previous crawl_get call, to continue from there
limit––Results in this page (default 100, max 10000)

No output schema declared.

No examples provided.

crawl_list ~78

List this account's crawls, newest first, with status and coverage (no results). Pass next_cursor as cursor for the next page; next_cursor is absent on the last page.

NameTypeReqDescription
cursorstring|null–next_cursor from a previous crawl_list call
limit––Crawls per page (default 20, max 100)

No output schema declared.

No examples provided.

crawl_results ~204

List the URLs a crawl kept, in the order it kept them, following the API's pages up to limit. Each result has url and, when the crawl has output_format, content_status (pending, fetched, failed) and, once fetched, content_url: pass it to crawl_content as content. While the crawl runs the list is partial (partial=true): call again later with the returned next_cursor to get only the URLs kept since. A stopped crawl can still add URLs from pages fetched before the stop for up to 10 minutes, so next_cursor stays set during that window: call again later. next_cursor is null once the crawl ended and every URL was read.

NameTypeReqDescription
crawl_idstringyesCrawl id returned by crawl_create (c_…)
cursorstring|null–next_cursor from a previous crawl_results call, to continue from there
limit––Max results to return in this call (default 100, max 10000)

No output schema declared.

No examples provided.

crawl_stop ~168

Stop a running crawl. No page still waiting is fetched or billed; pages already in flight finish. The URLs kept so far stay readable with crawl_results. A stopped crawl cannot resume. Idempotent: a crawl that already ended answers with its final status. A 429, 502, 503 or 504 is retried up to 3 times, waiting at most 50 seconds in all; a 503 crawl_busy means the stop was not saved yet, and if it persists, CRAWL_BUSY says when to call again. Pages in flight still finish, so coverage and results can keep growing for up to 10 minutes after the stop; read them with crawl_get and crawl_results.

NameTypeReqDescription
crawl_idstringyesCrawl id returned by crawl_create (c_…)

No output schema declared.

No examples provided.

crawl_wait ~165

Poll a crawl until it ends (completed, stopped, or failed) and return its status, its counts and the first 20 URLs (first_urls). For every result, call crawl_download (one file) or crawl_results (pages). A crawl that ends completed with no results and no error finished; it is not a failure (see crawl_get). If the wait runs out first, it returns the crawl with status running and a note: call crawl_wait again. A call that carries a progressToken gets a progress notification after each poll, with the status and counts.

NameTypeReqDescription
crawl_idstringyesCrawl id returned by crawl_create (c_…)
timeout––Max wait in seconds (default 50). Many MCP clients cancel a call after 60 s.

No output schema declared.

No examples provided.

extract ~769

Extract structured data from a webpage via Zenrows. Returns JSON fields (products, articles, listings) in "data" rather than a full page body. Set include_html to also get the page HTML. Modes: - auto (default): extract=auto, site-tailored Extract (currently free, billing may apply later). Works on any prepared domain, with any API key. Error REQS007 says why in "reason": - domain_not_prepared: call prepare_domain with the domain and 1 to 5 example URLs (one works; we recommend 3 or more of the same page type; a few minutes, no credits), poll prepared_domain_status about every 30 seconds until it says ready, then call extract again. - page_not_recognised: the domain is prepared, but this page is not one Extract extracts (a page with nothing to extract, such as a home page). Try a product, article or listing page instead; if this page should work, retry it once in a few minutes; otherwise use mode=css. Do not prepare the domain again. - domain_not_enabled: preparing does not help; use mode=css. - autoparse: deprecated general-purpose structured JSON on any domain; use only when asked for - css: css_extractor with an explicit selector map Requests use automatic mode (mode=auto) by default: Zenrows enables JS rendering and premium proxies only when the site needs them, and charges only for the configuration that succeeds. Pass just the URL. Set js_render or premium_proxy only to force a fixed configuration; that turns automatic mode off and bills every request at that configuration's cost. Check "empty" in the result: true means no field came back with a value. "noDataExtracted": true means Extract processed the page and found nothing to extract (not charged); retrying the same request returns the same answer. Full-page markdown, HTML and screenshots come from scrape.

NameTypeReqDescription
css_extractorstring|null–Required when mode=css. JSON map of field→selector, e.g. '{"title":"h1","price":".price"}'
fallback_autoparseboolean|null–Deprecated, has no effect: Extract no longer falls back to autoparse. Prepare the domain with prepare_domain instead.
include_htmlboolean|null–mode=auto only: also return the full page HTML. Default false; the HTML is most of the response size.
js_renderboolean|null–Force JS rendering on every request. Overrides automatic mode (mode=auto), which already renders when needed.
mode––Extraction mode: auto (extract=auto, default), css (requires css_extractor), or the deprecated autoparse
mode_autoboolean|null–Automatic mode (mode=auto) is on by default; leave unset. False sends a plain request without it.
premium_proxyboolean|null–Force premium residential proxies on every request (10x cost). Overrides automatic mode (mode=auto), which already uses premium proxies when a page needs them.
proxy_countrystring|null–ISO 3166-1 alpha-2 country code. Works in automatic mode (mode=auto); with js_render or mode_auto=false it requires premium_proxy.
urlstringyesThe webpage URL to extract from
wait––Milliseconds to wait after load. Requires js_render=true; without it, including in automatic mode, it may be ignored.
wait_forstring|null–CSS selector to wait for before extracting. Requires js_render=true; without it, including in automatic mode, it may be ignored.

No output schema declared.

No examples provided.

prepare_domain ~416

Prepare a domain so extract (mode=auto) works on it. Use this when extract returns error REQS007 with reason domain_not_prepared. Send the domain (the host, e.g. shop.example.com; a leading www. is dropped, and each host is prepared separately) and 1 to 5 example URLs of real pages you want extracted. One works; we recommend 3 or more of the same page type, so pass 3 or more when you can, up to 5 (e.g. 3 product pages: https://www.example.com/products/a, /products/b and /products/c), because Extract compares pages of one type to check the fields it found and fill in fields one page is missing. Several page types can share the 5 (e.g. 3 product pages + 2 search-result pages). It returns at once with status "preparing" and never waits; preparation takes a few minutes and costs no credits. Then call prepared_domain_status about every 30 seconds until status is "ready", and call extract again. Each new domain uses one slot of the account's monthly allowance (prepared_domain_status shows it). A domain already being prepared returns its current status; a domain already prepared returns "ready" at once with already_prepared: true and uses no slot. Do not call this for REQS007 with reason page_not_recognised: the domain is already prepared and preparing again does not change that answer.

NameTypeReqDescription
domainstringyesThe host to prepare, e.g. shop.example.com (a URL is accepted; a leading www. is dropped)
example_urlsarrayyes1 to 5 full URLs of real pages on this domain that you want to extract. One works; we recommend 3 or more of the same page type when you can
idempotency_keystring|null–Optional. Resending the same key with the same body returns the original submission

No output schema declared.

No examples provided.

prepared_domain_status ~122

Check a domain submitted with prepare_domain, and the account's monthly allowance. Returns status (preparing, ready or failed), state (the current step), roles (one per page kind), a failure code and detail when it failed, the allowance (used, limit, remaining) and "next": what to do now. Poll about every 30 seconds; preparation takes a few minutes. Call extract again once status is "ready". Costs no credits.

NameTypeReqDescription
domainstringyesThe domain passed to prepare_domain, e.g. example.com

No output schema declared.

No examples provided.

scrape ~842

Scrape any webpage and return its content using Zenrows (Fetch). Returns full-page content (markdown/HTML/PDF/screenshot). For structured JSON fields (products, articles, listings), the extract tool returns parsed fields instead of a full page body. By default the request uses automatic mode (mode=auto): Zenrows adds JS rendering or premium proxies per page only when a plain request does not return the page, and charges only for the configuration that succeeds. Pass just the URL. Set js_render or premium_proxy only to force a fixed configuration. Doing so switches off automatic mode, and every request is billed at that configuration's cost (premium_proxy with js_render is 25x a basic request). Examples: Default: { url: "https://example.com" } Geo: { url: "https://example.com", proxy_country: "US" } Forced: { url: "https://spa.com", js_render: true }

NameTypeReqDescription
autoparseboolean|null–Deprecated: general-purpose structured JSON from the page. Prefer the extract tool (extract=auto on a prepared domain, or css_extractor).
css_extractorstring|null–Extract specific elements using CSS selectors. JSON object mapping names to selectors, e.g. '{"title":"h1","price":".price-tag"}'. Returns JSON instead of full page content.
js_instructionsstring|null–JSON array of browser interactions to run before scraping. Works in automatic mode (mode=auto), which then renders the page and bills it at the JS rendering rate, or with js_render=true. Example: [{"…
js_renderboolean|null–Force JavaScript rendering on every request. Overrides automatic mode (mode=auto), which already renders JavaScript when a page needs it. Leave unset unless you need a fixed configuration.
outputsstring|null–Comma-separated list of data types to extract as structured JSON. Available: emails, headings, links, menus, images, videos, audios. Use '*' for all types. Returns JSON instead of full page content.
premium_proxyboolean|null–Force premium residential proxies on every request (10x credit cost). Overrides automatic mode (mode=auto), which already uses premium proxies when a page needs them. Leave unset unless you need a fi…
proxy_countrystring|null–Country for geo-targeted scraping. ISO 3166-1 alpha-2 code (e.g. 'US', 'GB', 'DE'). Works in automatic mode (mode=auto); if you set js_render without premium_proxy, it requires premium_proxy=true.
response_type––Output format. 'markdown' (default) preserves structure and is ideal for LLMs. 'plaintext' strips all formatting for pure text extraction. 'pdf' returns a PDF of the page. 'html' returns the raw HTML…
screenshotboolean|null–Capture an above-the-fold screenshot of the page. Returns an image instead of text content. Useful for visual verification or debugging.
screenshot_fullpageboolean|null–Capture a full-page screenshot including content below the fold. Returns an image instead of text content.
screenshot_selectorstring|null–Capture a screenshot of a specific element using a CSS selector. Example: ".product-card". Returns an image instead of text content.
urlstringyesThe webpage URL to scrape
wait––Milliseconds to wait after page load before capturing content. Max 30000 (30s). Requires js_render=true; without it, including in automatic mode, it may be ignored.
wait_forstring|null–CSS selector to wait for before capturing. Use when key content loads after the initial page render. Requires js_render=true; without it, including in automatic mode, it may be ignored.

No output schema declared.

No examples provided.

Install

How do I install the io.github.ZenRows/zenrows-mcp server?

io.github.ZenRows/zenrows-mcp runs locally as an npm package, launched with npx -y @zenrows/mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

npm · @zenrows/mcp

# add to Claude Code
claude mcp add zenrows-zenrows-mcp -- npx -y @zenrows/mcp
Common questions

What is the io.github.ZenRows/zenrows-mcp server?

io.github.ZenRows/zenrows-mcp is listed in the public MCP registry as io.github.ZenRows/zenrows-mcp. Zenrows MCP server: Fetch, Extract, Batch, and Browser Sessions for AI agents. This page covers its npm package (@zenrows/mcp).

Is the io.github.ZenRows/zenrows-mcp server safe to use?

io.github.ZenRows/zenrows-mcp scores 84 out of 100 on VerifyMCP. We recorded 2 known advisories against it as of 4 November 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

What tools does the io.github.ZenRows/zenrows-mcp server expose?

io.github.ZenRows/zenrows-mcp exposes 63 tools: account_signup_answer, scrape, extract, prepare_domain, prepared_domain_status, and 58 more. Their descriptions and schemas cost roughly 9,933 tokens of context every time the server is loaded.

Is the io.github.ZenRows/zenrows-mcp server still maintained?

io.github.ZenRows/zenrows-mcp is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

What licence is the io.github.ZenRows/zenrows-mcp server under?

io.github.ZenRows/zenrows-mcp declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.