Zenrows Mcp

io.github.ZenRowsPublished by GitHub account ZenRowsv2.19.0Updated Nov 7, 2026

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

VerifiedStreamable HTTPWeb executableWeb Search & ScrapingBrowser Automation

Overview

AI-generated overview

Zenrows MCP lets an assistant scrape pages, extract structured JSON, run large batch and crawl jobs, and drive hosted browser sessions on the live web.

What you can do
  • Scrape a single page as Markdown, HTML, plain text, PDF or screenshot, or extract structured JSON fields from product, article and listing pages
  • Prepare a domain for automatic extraction and poll its preparation status
  • Submit, wait on, page through, pause, resume, rerun, cancel and delete cloud Batch jobs of many URLs, and read individual task results
  • Start crawls from one URL, follow links within the domain, read or download the kept pages, and stop a crawl
  • Drive a hosted browser session: navigate, click, hover, type, fill, select, check, press keys, scroll and drag
  • Check the account's credit allowance and usage
When to use it
Use it when an assistant needs live web content that ordinary fetching cannot reach, such as bot-protected sites, or when you want structured fields rather than raw page bodies. It also fits large URL lists handled asynchronously and multi-step pages that need clicks or typing.
Requirements
Either the remote endpoint at mcp.zenrows.com over Streamable HTTP, or the local npm package run with npx, which needs Node.js. Tool calls need a Zenrows API key: the ZENROWS_API_KEY environment variable locally, or an Authorization Bearer token or OAuth remotely. Without a key, the local server can auto-create a free account unless ZENROWS_AUTO_SIGNUP is false; the remote server never does.
Before you install
Usage is billed in credits: 1 basic, 5 with JS rendering, 10 with premium proxy, 25 with both, and each crawled page counts as one scrape, so large batches and crawls spend real money. Batch and crawl tools create, pause, rerun and permanently delete jobs and results. Auto-signup sends a per-machine id and a guessed host agent name unless ZENROWS_TELEMETRY is off, and stores the key under the home directory. Keep the API key secret.

Installation

In SourceWeft

  1. Open Zenrows Mcp in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.ZenRows%2Fzenrows-mcp/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "zenrows": {
      "type": "http",
      "url": "https://mcp.zenrows.com/mcp"
    }
  }
}

README

[Zenrows MCP]

Zenrows MCP Server

The Zenrows MCP (Model Context Protocol) server is the standard way AI systems use Zenrows' web data infrastructure. A single connection gives your AI assistant, agent, or application reliable, real-time access to the live web, including the protected web.

[npm version] [MIT License]

📚 Full documentation: docs.zenrows.com/mcp/overview


Why Zenrows MCP

  • Reach sites that normally block bots. Get reliable access to protected sites at scale, without building anti-bot handling yourself.
  • Managed web data infrastructure. Proxy rotation, headless browser orchestration, anti-bot handling, and session management run on Zenrows' infrastructure.
  • Plug into any AI you already use. Works with any MCP client, including AI assistants, agent frameworks, AI SDKs, IDE plugins, and custom applications.
  • Plain English, no scraping code. Describe the task naturally and the AI picks the right tool. No selectors, no proxy management, no anti-bot tuning.

Quick start

Zenrows MCP supports two transport options. Both expose the same set of tools and capabilities. Pick the one that fits your client.

What works without a key:

  • Local (npx @zenrows/mcp): no signup, no key. The first tool call creates a Free account (5,000 credits/month) that you can claim later, keeping the key and its usage. Opt out with ZENROWS_AUTO_SIGNUP=false. In CI, set ZENROWS_API_KEY as a secret or persist ZENROWS_HOME between runs.
  • Remote (https://mcp.zenrows.com/mcp): initialize and tools/list answer without a key; tool calls need one (OAuth in clients that support it, or a Bearer key). The remote server never creates accounts by itself. A headless agent can create a Free account with POST https://app.zenrows.com/api/agent/signup and use the returned apiKey as the Bearer token. The endpoint may first answer 428 with a short question; POST {"challenge_id": "...", "answer": "..."} back to the same URL to get the key.

Remote MCP server

Use the hosted Zenrows MCP server when your AI application calls an LLM API directly. The server runs on Zenrows' infrastructure, so there is nothing to install, configure, or update.

Server URL:

https://mcp.zenrows.com/mcp

Transport: Streamable HTTP

Authentication: OAuth or API key as Bearer token. Pass your Zenrows API key in the Authorization header on every request (or complete OAuth in clients that support it).

Authorization: Bearer YOUR_ZENROWS_API_KEY

Most MCP clients accept this through an authorization shorthand field on the tool config and forward it as the Bearer token automatically. Some clients use a free-form headers field instead. Either approach works.

Remote MCP does not auto-create accounts. initialize and tools/list work without a key; a keyless tool call gets a 401 whose body names the signup endpoint (agent_signup). Use OAuth “Create Free account” in the client, pass an existing API key, or, from a headless agent, POST https://app.zenrows.com/api/agent/signup (anonymous; returns apiKey, or first a 428 question to answer with challenge_id and answer; per-network limits apply) and retry with Authorization: Bearer <apiKey>.

Example: OpenAI Responses API
python
import osfrom openai import OpenAI
ZENROWS_API_KEY = os.environ["ZENROWS_API_KEY"]client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
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",            "authorization": ZENROWS_API_KEY,            "require_approval": "never",        }    ],    input="Visit https://news.ycombinator.com/ and summarize the three most recent posts.",)
print(response.output_text)

For the full walkthrough with framework-specific examples, see the Remote MCP server docs.

Local MCP server

Use the local stdio configuration when your MCP client runs the server as a local subprocess instead of calling a remote URL. This is the standard setup for desktop AI tools and IDE plugins, including Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Zed, and JetBrains IDEs.

Package: @zenrows/mcp on npm

Authentication:

  1. ZENROWS_API_KEY environment variable, or
  2. Key previously stored in ~/.zenrows/secrets.json, or
  3. Auto-signup (default): if neither is set, the server still starts, and the first tool call provisions a Free plan account via POST /api/agent/signup, persists the key + claim metadata under ~/.zenrows/ (secrets.json + account.json, mode 0600), and prints a claim URL on stderr. Zenrows may first ask a short question: the tool call then returns it, and the agent answers with the account_signup_answer tool, then retries. If signup is refused (for example the per-network cap in CI), that tool call returns an error saying when to retry; in CI, set ZENROWS_API_KEY as a secret or persist ZENROWS_HOME between runs. Opt out with ZENROWS_AUTO_SIGNUP=false: tool calls then return an error until a key is set. The signup request also sends a random per-machine id (shared with the Zenrows CLI) and a guess at the host agent (claude-code, cursor, …) so signups can be attributed; set ZENROWS_TELEMETRY=off to omit both.

Requirements: Node.js installed (for npx to work).

Configuration (with your own key):

json
{  "mcpServers": {    "zenrows": {      "command": "npx",      "args": ["-y", "@zenrows/mcp"],      "env": {        "ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY"      }    }  }}

Zero-config (auto-signup):

json
{  "mcpServers": {    "zenrows": {      "command": "npx",      "args": ["-y", "@zenrows/mcp"]    }  }}

Optional: ZENROWS_CRAWL_API_BASE sets the Crawl API base URL for the crawl_* tools (default https://api.zenrows.com/v1). Set it only to point those tools at another deployment.

Optional: ZENROWS_BATCH_API_BASE sets the Batch API base URL for the batch_* tools (default https://async.api.zenrows.com/v1). Set it only to point those tools at another deployment.

Optional: ZENROWS_FETCH_RETRIES lets scrape and extract retry, up to that many times, a call the API did not serve. The default 0 sends each call once. They retry a 429, 502, 503 or 504, and a connection that failed before the request was sent (refused, name resolution, unreachable network or host, connect timeout); every other status or error is returned at once, because the API may have received the request. Each wait is the Retry-After in whole seconds (clamped to 250 ms - 30 s), or else random between 250 ms and 1 s, 2 s, 4 s, ... up to 30 s. A retry starts only if its wait ends within 50 s of the first request, because MCP clients cancel a tool call at about 60 s; a retry that starts near 50 s can still run past that timeout. Each retry writes one line to stderr. The value is read once at start, and anything other than a whole number of 0 or more stops the server. The hosted server retries only if its operator sets it.

The exact location of this config varies by client. See the per-client setup guides for the file path for your client.


Tools

The Zenrows MCP exposes these tool families:

ToolPurpose
scrapeFull-page content → Markdown, plain text, HTML, PDF, or screenshot (plus helper outputs).
extractStructured JSON (extract=auto or css_extractor; autoparse is deprecated) + optional stealth flags. extract=auto is currently free (billing may apply later) and works on any prepared domain, with any API key. Returns parsed fields only; set include_html: true to also get the page HTML. Error REQS007 says why in reason: domain_not_prepared → call prepare_domain (the error carries its arguments; one URL works; we recommend 3 or more of the same page type, so add them to example_urls, up to 5); 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, retry it once in a few minutes if it should work, otherwise use css_extractor, and do not prepare again; domain_not_enabled → preparing does not help, use css_extractor. The error never offers autoparse, and the tool never falls back to it: fallback_autoparse is deprecated and has no effect (when it is true, the result carries a note saying it was ignored).
prepare_domain / prepared_domain_statusPrepare a domain for extract (mode=auto) after a REQS007 with reason domain_not_prepared. prepare_domain takes the domain and 1 to 5 example URLs. One works; we recommend 3 or more of the same page type (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" (it never waits), and costs no credits. Preparation takes a few minutes: poll prepared_domain_status about every 30 seconds (status, current step, failure code, the account's monthly allowance used / limit / remaining, and next, the step to take now) until status is ready, then call extract again. Each new domain uses one allowance slot; a domain that is already prepared answers ready at once with already_prepared: true and uses none. The domain is the host: shop.example.com and example.com are prepared separately, and a leading www. is dropped.
batch_create / batch_status / batch_wait / batch_results / batch_content / batch_list / batch_cancel / batch_pause / batch_resume / batch_rerun / batch_deleteCloud Batch API fan-out (async.api.zenrows.com). Same credits as Fetch. Not browser_batch. See Batch tools below.
crawl_create / crawl_get / crawl_results / crawl_content / crawl_download / crawl_list / crawl_stop / crawl_waitCrawl API (api.zenrows.com/v1/crawls). New. Crawl is still evolving: new features are coming, limits may be tuned, and the changelog announces each change. Give one start URL, get back the URLs behind it on the same registrable domain (subdomains count), optionally with each page's HTML (output_format: "html"). crawl_create returns at once; call crawl_wait (counts and the first 20 URLs), then crawl_download to save every result to a local JSON-lines file (default ~/.zenrows/crawls/<crawl_id>.jsonl; a path must stay inside ~/.zenrows/crawls; an existing file is kept unless overwrite: true; the hosted server writes no file and returns the download URL and a curl command instead), or crawl_results to page through them. Leave depth out of crawl_create for no depth limit: the crawl follows links until max_pages or max_items (default 100 each) stops it, and each page fetched is billed as one scrape. Set idempotency_key on crawl_create to make a retry safe. crawl_content takes crawl_id and content_id (a content id or a result's content_url). Reads retry on 429, 502, 503, 504 and network errors (up to 3 times, honoring Retry-After up to 30 seconds); crawl_create retries only with an idempotency_key, and never on 429; crawl_stop retries on 429, 502, 503 and 504 (repeating a stop is safe; a 503 crawl_busy means the stop was not saved yet), up to 3 times and 50 seconds at most in all, and returns CRAWL_BUSY if a crawl_busy persists. May return CRAWL_NOT_ENABLED (Crawl is not enabled for this account).
browser_*30+ tools for full browser automation (navigation, clicks, forms, JS, cookies, tabs, sessions).

Batch tools

A Batch job scrapes many URLs asynchronously. The usual flow: batch_create, then batch_wait until the run ends, batch_results to page through the rows, and batch_content to read one task's page.

  • batch_create submits one closed job with all its tasks, up to 100,000 per call. Each task takes a url (http or https, up to 2,048 characters), plus optional external_id (up to 128 characters of letters, digits, ., _ and -; need not be unique), metadata (up to 20 string-to-string entries, 4 KiB in all), method and body for POST tasks (body up to 16 KiB; POST is for read-only queries, since the target can receive a POST more than once), and scrape params, in zenrows_params or flat on the task. An unknown task field fails the call by name, before anything is sent. response_type takes markdown, plaintext or pdf; leave it out for HTML. Tasks use mode=auto unless you force js_render or premium_proxy. Every call sends an Idempotency-Key, generated unless you set idempotency_key: a repeat with the same key and body within 24 hours returns the original job instead of a second one, and a different body is BATCH_IDEMPOTENCY_CONFLICT. An error reply carries the key to retry with. Set wait: true to return when the run ends or pauses, or when wait_timeout (seconds, default 50) passes.
  • batch_status and batch_wait report the latest run's status (pending, running, completed, stopped, failed) and stats. batch_wait polls until the run ends, returns at once with paused: true on a paused run, and tolerates transient errors. Its timeout is in seconds (default 50, under the 60 seconds after which many clients cancel a call); when it runs out, the reply is the current state with ok: true, timed_out: true and a note, not an error. A call with a progressToken gets a progress notification after each poll. A run still writing its tasks (submits of 10,000 or more, and every rerun) has ingesting: true.
  • batch_results returns up to limit rows (default 100, max 1,000) and a next_cursor; pass it back as cursor until it is null. Filter with status: all, successful, failed or processing. Pass run_id for an earlier run. A row's result_url is presigned for 2 hours: list again, or use batch_content, for a fresh one. Results are kept 14 days by default; after that a successful row reads as expired, with no result_url.
  • batch_content returns one task's result. Text (HTML, Markdown, plain text, JSON) comes back inline, cut to max_chars (default 20,000); a PDF comes back as its presigned result_url; on a large run, finding it can take more than one call, and the reply then carries lookup_cursor to pass back. A task not finished yet is BATCH_TASK_NOT_READY, a failed one BATCH_TASK_FAILED (with task_error), an expired one BATCH_RESULT_EXPIRED.
  • batch_list lists the account's jobs, newest first (default 20 per page, max 100).
  • batch_cancel stops the current run for good: no waiting task starts, in-flight tasks finish and bill, results stay. batch_pause halts it until batch_resume; a paused run still counts as an active job. All three are idempotent, and on a run that already ended they answer with its final state and already_ended: true.
  • batch_rerun starts a new run once the latest one ended. By default it reruns the failed and pending tasks and carries the others over: the recovery after a run that ran out of credits or was cancelled. Pass status for other task statuses, or full: true to scrape every task again. A partial rerun works up to 10 days after the run started. It sends an Idempotency-Key.
  • batch_delete deletes a job with its runs and results, and frees its active-job slot.

Costs: the same credits as the regular API, per successful task: 1 basic, 5 with js_render, 10 with premium_proxy, 25 with both; mode=auto bills each task at the configuration that worked. Failed tasks are not billed, except target 404 and 410 answers. An account runs 3 active jobs at once by default, shared with Crawl.

A run that ends failed answers ok: false, with error.code the same as the matching HTTP error (BATCH_INSUFFICIENT_CREDITS, BATCH_KEY_CAP_REACHED, or AUTH_INVALID for an inactive subscription; BATCH_RUN_FAILED otherwise), error.server_code (the failure_reason), error.job_id and advice. Fix the cause, then batch_rerun.

Errors carry code, message, detail, server_code (the Batch API's own code) and, for a call about one job, job_id:

CodeWhen
BATCH_INVALID_REQUEST400: the request was rejected; invalid_tasks lists the first bad tasks. Nothing was created.
AUTH_INVALID401: the API key was rejected, or, with server_code subscription_inactive, the key is valid but the subscription needs fixing in Billing.
BATCH_INSUFFICIENT_CREDITS402: the account has no credits left.
BATCH_KEY_CAP_REACHED402: this API key reached one of its credit caps; the account's other keys still work.
BATCH_ACCESS_DENIED403: Batch is not enabled for this account or key. Do not retry.
BATCH_NOT_FOUND404: no such job, run or task for this account (a deleted job included).
BATCH_RUN_ENDED, BATCH_RUN_NOT_FINISHED, BATCH_NO_MATCHING_TASKS, BATCH_RERUN_WINDOW_EXPIRED, BATCH_NO_PRIOR_RUN, BATCH_IDEMPOTENCY_CONFLICT, BATCH_TASK_NOT_READY, BATCH_CONFLICT409, by server_code.
BATCH_RESULT_EXPIRED410: past the job's retention.
BATCH_TASK_FAILED422: the task failed; task_error holds its error.
BATCH_QUOTA_EXCEEDED429: a job limit more credits do not raise (3 active jobs, shared with Crawl; 5 scheduled jobs).
BATCH_RATE_LIMITEDAny other 429.
BATCH_FAILEDAny other HTTP error.
BACKEND_UNAVAILABLENo response: a network error or timeout, after retries.

Reads retry on 429, 500, 502, 503, 504 and network errors, up to 3 times, honoring Retry-After up to 30 seconds. batch_create and batch_rerun retry on 500, 502, 503, 504, network errors and a 429 with Retry-After, always with the same Idempotency-Key; never on the job-limit 429. On BATCH_IDEMPOTENCY_CONFLICT for the same request, keep the key, check batch_list and retry later: a new key can create a second, billed job.

Schedules, webhooks, results exports, CSV inputs, open jobs (adding tasks over time), runs and task history are available through the Batch API and the Zenrows SDKs only.

The AI selects the right tool from your prompt. You don't call tools directly in code.

See the full tool reference for every tool, parameter, and return value.


Development

bash
git clone https://github.com/ZenRows/zenrows-mcpcd zenrows-mcpnpm installcp .env.example .env   # Optional: add your API key (stdio can auto-signup)npm run dev            # Run with .env loaded (requires Node.js 20.6+)npm run build          # Compile to dist/npm run inspect        # Open the MCP inspector UI

Running the Crawl end-to-end test

npm test stays offline. npm run test:e2e drives the crawl_* tools through a real MCP client against a live Crawl API: it creates a small crawl (depth 1, at most 3 URLs and 5 pages) of the start URL you give it, waits for it, reads its results and one page, lists crawls, stops the ended crawl, and checks the not-found error. It is skipped unless the first three variables are set:

  • ZENROWS_API_KEY: a key with Crawl access.
  • ZENROWS_CRAWL_API_BASE: the Crawl API base, e.g. https://api.zenrows.com/v1. Point it at a local or staging deployment to test against that instead.
  • ZENROWS_E2E_CRAWL_URL: the start URL to crawl, a listing page whose links lead to item pages.
  • ZENROWS_E2E_CRAWL_INCLUDE (optional): an include pattern; when set, the test checks that every result URL contains it.
bash
export ZENROWS_API_KEY=...            # a key with Crawl accessexport ZENROWS_CRAWL_API_BASE=https://api.zenrows.com/v1export ZENROWS_E2E_CRAWL_URL=https://example.com/products/export ZENROWS_E2E_CRAWL_INCLUDE=/product/npm run test:e2e

The crawl is billed to the key's account (at most 5 page fetches), and it counts against the account's limit of active jobs (3 by default), shared with its Batch jobs. When the account has reached that limit, the test waits and retries for up to 5 minutes.

Releases

Merge PRs with Conventional Commit titles, for example fix(extract): ... or feat(crawl): .... fix, feat and perf need a scope.

After each merge to main, release-please opens or updates the PR chore: release X.Y.Z. It sets the version in package.json, package-lock.json and server.json. Do not change these by hand.

To release, merge that PR. The Release workflow runs the checks, creates the tag vX.Y.Z and the GitHub release, and starts the Publish workflow. That workflow publishes to npm and the MCP Registry, pushes the GHCR image and deploys the hosted server.

To publish an existing tag again, run the Release workflow by hand from main with that tag.

If the checks fail after the release PR is merged

The release PR runs CI like any other PR. After you merge it, the release run checks the code again before it tags and publishes. If that check fails, main already has the new version, but there is no tag, and the merged release PR keeps the autorelease: pending label, which blocks the next release PR. To recover:

  1. Fix the problem in a normal PR and merge it.
  2. Create the release on that fix commit, with the version from .release-please-manifest.json: gh release create vX.Y.Z --target <fix commit> --title vX.Y.Z --generate-notes --notes-start-tag <previous tag>
  3. On the merged release PR, replace the label autorelease: pending with autorelease: tagged.
  4. Run the Release workflow by hand from main with the tag vX.Y.Z.

Pull requests and issues are welcome.


Resources


License

MIT

Source: README.md at commit 8fbaebf

Tools

62

scrape

Scrape Webpage

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 }

Parametersurlrequiredwaitoutputswait_forautoparsejs_renderscreenshotcss_extractorpremium_proxyproxy_countryresponse_typejs_instructions+2 more

extract

Extract Structured Data

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.

Parametersurlrequiredmodewaitwait_forjs_rendermode_autoinclude_htmlcss_extractorpremium_proxyproxy_countryfallback_autoparse

prepare_domain

Prepare a Domain for Extract

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.

Parametersdomainrequiredexample_urlsrequiredidempotency_key

prepared_domain_status

Prepared Domain Status

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.

Parametersdomainrequired

batch_create

Create Batch Job

Submit a Batch job: many URLs scraped asynchronously in the cloud (Zenrows Batch API). Suited to large URL lists; scrape and extract handle single pages. Not the same as browser_batch. Tasks use automatic mode (mode=auto) by default: Zenrows enables JS rendering and premium proxies only on the pages that need them. Pass just the URLs. Set js_render or premium_proxy (job-level, or per task) only to force a fixed configuration; that turns automatic mode off for those tasks. Cost: the same credits as the regular API, per successful task: 1 for a basic request, 5 with js_render, 10 with premium_proxy, 25 with both; mode=auto bills each task at the configuration that worked. Failed tasks are not billed, except target 404 and 410 answers, billed as on the regular API. Limits: up to 100,000 tasks per call; 3 active jobs per account by default, shared with Crawl (BATCH_QUOTA_EXCEEDED past that); a job frees its slot when its run ends. Creates a closed job with every task in this one call. Each task takes a url plus optional external_id, metadata, method/body (POST tasks) and scrape params. Every call sends an Idempotency-Key, so the bounded retries on network errors and 5xx never create a second job; an error reply carries the key to retry with. Returns job_id, status and stats at once; the job runs asynchronously. Then: batch_wait until it ends, batch_results to list rows (paged), batch_content to read one task's page. A run that ends failed (out of credits, inactive subscription, key cap) answers ok:false with error.code and server_code; batch_rerun recovers it. Submits of 10,000+ tasks keep writing tasks for a while (ingesting:true). Errors: BATCH_INVALID_REQUEST (400, server_code and invalid_tasks say what), BATCH_INSUFFICIENT_CREDITS (402), BATCH_KEY_CAP_REACHED (402, this key's cap), BATCH_QUOTA_EXCEEDED (429, job limit), AUTH_INVALID (401; server_code subscription_inactive means fix Billing), BATCH_ACCESS_DENIED (403, do not retry), BACKEND_UNAVAILABLE. Schedules

Parametersurlswaittasksjs_rendermode_autowait_timeoutpremium_proxyproxy_countryresponse_typezenrows_paramsidempotency_keywait_timeout_ms

batch_status

Batch Job Status

Get a Batch job's latest run: job_id, run_id, status (pending, running, completed, stopped, failed), stats (total, completed, successful, failed) and the job. A failed run answers ok:false with error.code BATCH_INSUFFICIENT_CREDITS, BATCH_KEY_CAP_REACHED, AUTH_INVALID (subscription_inactive) or BATCH_RUN_FAILED, error.server_code (the failure_reason) and failure_detail; batch_rerun recovers it once fixed. A paused run has paused:true; one still writing its tasks has ingesting:true.

Parametersjob_idrequired

batch_wait

Wait for Batch Job

Poll a Batch job until its run ends (completed, stopped or failed) or pauses, and return its status and stats like batch_status. If the wait runs out first, it returns the current state with ok:true, timed_out:true and a note: call batch_wait again. A paused run returns at once with paused:true (it does not move until batch_resume). A failed run answers ok:false with error.code and server_code (insufficient_credits, subscription_inactive or api_key_cap_reached). Transient poll errors are retried. A call that carries a progressToken gets a progress notification after each poll.

Parametersjob_idrequiredtimeouttimeout_ms

batch_results

Batch Job Results

List a Batch run's result rows, a page at a time: up to limit rows (default 100, max 1000) and next_cursor. Pass next_cursor back as cursor to continue; it is null once every row was read. Each row has task_id, external_id, url, metadata, status (successful, failed, pending, processing, expired), type, result_url when successful, and error when failed. A result_url is presigned and expires after 2 hours: call batch_results again for a fresh one, or read the body with batch_content. Results are kept 14 days by default; after that a successful row reads as status "expired", with no result_url, and batch_content answers BATCH_RESULT_EXPIRED. Filter with status: all (default), successful, failed, or processing (not finished yet). A sparse filter can return fewer rows than limit with next_cursor still set: keep paging. While the run is still running or writing its tasks, the list is partial.

Parameterslimitcursorjob_idrequiredrun_idstatus

batch_content

Batch Task Content

Read one Batch task's stored result, by job_id and task_id (from batch_results). A text result (HTML, Markdown, plain text, JSON) comes back inline, cut to max_chars (default 20000); a final note says when it was cut. A binary result (a PDF) is not inlined: the reply gives its result_url, presigned for 2 hours. Finding that URL reads the run's results; on a large run one call may run out of time first and answer with lookup_cursor: call batch_content again with it to go on. BATCH_TASK_NOT_READY: the task has not finished; BATCH_TASK_FAILED: it failed (task_error holds its error); BATCH_RESULT_EXPIRED: past the job's retention (14 days by default); rerun to scrape it again.

Parametersjob_idrequiredrun_idtask_idrequiredmax_charslookup_cursor

batch_list

List Batch Jobs

List this account's Batch jobs, newest first, each with its latest run's status and stats. Filter by status (open or closed) and type (regular or scheduled). Pass next_cursor as cursor for the next page; a page can hold fewer than limit jobs while next_cursor is still set, so keep paging until it is null. Deleted jobs are not listed.

Parameterstypelimitcursorstatus

batch_cancel

Cancel Batch Job

Stop a Batch job's current run for good. No task still waiting is started or billed; tasks already in flight finish (and bill). Results so far stay readable with batch_results. Tasks that never started stay pending: batch_rerun (default failed and pending) runs them later. Idempotent: a run that already ended answers with its final status and already_ended:true. To halt without ending the run, use batch_pause.

Parametersjob_idrequired

batch_pause

Pause Batch Job

Pause a Batch job's current run: it picks up no new tasks until batch_resume. Tasks already in flight still finish and bill; for a hard cost stop use batch_cancel. A paused run still counts as an active job. Idempotent; a run that already ended answers with its final status and already_ended:true.

Parametersjob_idrequired

batch_resume

Resume Batch Job

Resume a paused Batch run: it picks up its remaining tasks again, billed as they finish. Idempotent on a run that is not paused; a run that already ended answers with its final status and already_ended:true. Needs credits: BATCH_INSUFFICIENT_CREDITS or BATCH_KEY_CAP_REACHED otherwise.

Parametersjob_idrequired

batch_rerun

Rerun Batch Job

Start a new run of a Batch job whose latest run ended (completed, stopped or failed). By default it reruns only the tasks that failed or never started (status ["failed", "pending"]), and carries the others' results over: the recovery after a run that failed on credits or was cancelled. Pass status to pick other task statuses, or full:true to scrape every task again. Rerun tasks are billed like new ones. A rerun of only some tasks works up to 10 days after the run started (BATCH_RERUN_WINDOW_EXPIRED after that; full:true still works). BATCH_NO_MATCHING_TASKS: no task has those statuses. BATCH_RUN_NOT_FINISHED: the latest run is still in flight. Sends an Idempotency-Key, so a retry never starts two runs. Returns the new run at once (it writes its tasks first, ingesting:true); call batch_wait.

Parametersfulljob_idrequiredstatusidempotency_key

batch_delete

Delete Batch Job

Delete a Batch job with all its runs and results, for good. A run in flight stops picking up tasks at once, and the job frees its active-job slot. The job reads as not found right away; storage is cleaned up in the background. Idempotent. To keep the results, use batch_cancel instead.

Parametersjob_idrequired

crawl_create

Create Crawl

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.

Parametersurlrequireddepthwebhookdiscoverymax_itemsmax_pagesoutput_formatidempotency_keyexclude_patternsinclude_patterns

crawl_get

Get Crawl

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.

Parameterslimitcursorcrawl_idrequired

crawl_results

Crawl Results

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.

Parameterslimitcursorcrawl_idrequired

crawl_content

Crawl Page Content

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.

Parameterscrawl_idrequiredmax_charscontent_idrequired

crawl_list

List Crawls

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.

Parameterslimitcursor

crawl_stop

Stop Crawl

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.

Parameterscrawl_idrequired

crawl_wait

Wait for Crawl

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.

Parameterstimeoutcrawl_idrequired

crawl_download

Download Crawl

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.

Parameterspathcrawl_idrequiredoverwrite

account_usage

Check Credit Usage

Read the current plan's credit allowance and how much of it is spent. Useful before a large batch, or after a 402 / AUTH004, to see whether the account has credits left and when the allowance renews. The call is free and does not use a concurrency slot. Credit costs per request: 1 basic, 5 js_render, 10 premium_proxy, 25 both. AUTH004 ("usage exceeded") means this allowance is spent until it renews at the end of the billing period (period_ends_at in this tool's response). Plans and credit options are described at https://www.zenrows.com/pricing. AUTH006 is the concurrency limit, not a credit limit. AUTH014 (Batch: api_key_cap_reached) means this API key hit one of its own credit caps while the account still has credits; other keys keep working. The response's api_key.caps lists the calling key's caps: window, credits, used_credits, remaining_credits, resets_at, and unavailable (usage could not be read). Caps are managed at https://app.zenrows.com/settings/api-keys.

Takes no parameters.

browser_navigate

Open Browser & Navigate

Open a Zenrows Browser Sessions session and navigate to a URL. This is the entry point for all browser automation. It creates a new session backed by Zenrows' hosted browser infrastructure and navigates to the given URL in one step. Returns a session_id that every other browser_* tool takes. The session holds one concurrency slot until browser_close closes it. When to use options: - proxy_country: geo-restricted content (ISO 3166-1 alpha-2, e.g. "US", "DE") - proxy_region: world region for geo-targeted proxy (eu, na, ap, sa, af, me)

Parametersurlrequiredproxy_regionproxy_country

browser_close

Close Browser Session

Close a browser session and free all associated resources, including the concurrency slot it holds.

Parameterssession_idrequired

browser_go_back

Go Back

Navigate back to the previous page in the browser history.

Parameterssession_idrequired

browser_go_forward

Go Forward

Navigate forward to the next page in the browser history.

Parameterssession_idrequired

browser_reload

Reload Page

Reload the current page of a browser session.

Parameterssession_idrequired

browser_click

Click Element

Click an element on the page using a CSS selector.

Parametersselectorrequiredsession_idrequired

browser_hover

Hover Element

Move the mouse cursor over an element to trigger hover effects.

Parametersselectorrequiredsession_idrequired

browser_type

Type Text

Type text into an input element. Appends to existing content unless clear_first is set.

Parameterstextrequiredselectorrequiredsession_idrequiredclear_first

browser_fill

Fill Input

Clear an input field and set its value, replacing what was there (browser_type appends instead).

Parametersvaluerequiredselectorrequiredsession_idrequired

browser_select_option

Select Option

Select an option from a <select> dropdown element.

Parametersvaluerequiredselectorrequiredsession_idrequired

browser_check

Check Checkbox

Check a checkbox or radio button.

Parametersselectorrequiredsession_idrequired

browser_uncheck

Uncheck Checkbox

Uncheck a checkbox, found by CSS selector, in a browser session.

Parametersselectorrequiredsession_idrequired

browser_focus

Focus Element

Move focus to an element, triggering focus-dependent UI changes.

Parametersselectorrequiredsession_idrequired

browser_press_key

Press Key

Press a keyboard key. Examples: "Enter", "Tab", "Escape", "ArrowDown", "Control+a".

Parameterskeyrequiredsession_idrequired

browser_scroll

Scroll Page

Scroll the page in a given direction.

Parametersdistancedirectionrequiredsession_idrequired

browser_drag

Drag Element

Drag an element from its current position to a target element.

Parameterssession_idrequiredsource_selectorrequiredtarget_selectorrequired

browser_get_accessibility_tree

Get Accessibility Tree

Get the ARIA accessibility tree of the current page as readable text. Shows the page structure and the selectors needed to interact with elements, in fewer tokens than a screenshot. The tree shows all interactive elements (buttons, inputs, links) with their roles, labels, and states — everything needed to drive browser interactions.

Parameterssession_idrequired

browser_get_url

Get Current URL

Get the current URL of the browser session.

Parameterssession_idrequired

browser_get_title

Get Page Title

Get the title of the current page.

Parameterssession_idrequired

browser_get_text

Get Text Content

Get the visible text content of an element or the entire page body.

Parametersselectorsession_idrequired

browser_get_attribute

Get Element Attribute

Get the value of a specific attribute on an element.

Parametersselectorrequiredattributerequiredsession_idrequired

browser_get_html

Get HTML

Get the HTML source of an element or the full page.

Parametersselectorsession_idrequired

browser_query_selector_all

Query All Matching Elements

Find all elements matching a CSS selector and return their text, HTML, and attributes.

Parametersselectorrequiredsession_idrequired

browser_screenshot

Take Screenshot

Take a screenshot of the current page or a specific element. Use for visual verification or when the accessibility tree is not sufficient.

Parametersselectorfull_pagesession_idrequired

browser_generate_pdf

Generate PDF

Render the current page as a PDF document.

Parametersscalelandscapesession_idrequiredprint_background

browser_wait_for_selector

Wait for Element

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).

Parametersvisibleselectorrequiredsession_idrequired

browser_wait_for_navigation

Wait for Navigation

Wait for the page to navigate to a new URL. It waits for the next navigation event, so a navigation that already finished is not seen and the call waits until timeout_ms. Pages that update without changing URL (AJAX/SPA) fire no navigation event. Optional timeout_ms (default 30000ms).

Parameterssession_idrequiredtimeout_ms

browser_wait

Wait

Wait for a fixed duration. browser_wait_for_selector waits only until an element appears.

Parametersmsrequiredsession_idrequired

browser_evaluate

Evaluate JavaScript

Execute JavaScript in the browser context and return the result. The script can change or delete the session's page state, such as cookies, storage and form data.

Parametersscriptrequiredsession_idrequired

browser_get_cookies

Get Cookies

Get all cookies for the current page.

Parameterssession_idrequired

browser_set_cookies

Set Cookies

Set one or more cookies in the browser session. A cookie with the same name, domain and path replaces the existing one.

Parameterscookiesrequiredsession_idrequired

browser_clear_cookies

Clear Cookies

Clear all cookies for the current browser session.

Parameterssession_idrequired

browser_get_local_storage

Get Local Storage Item

Read one localStorage value in the current page context.

Parameterskeyrequiredsession_idrequired

browser_set_local_storage

Set Local Storage Item

Write one localStorage value in the current page context. An existing value under the same key is replaced.

Parameterskeyrequiredvaluerequiredsession_idrequired

browser_clear_local_storage

Clear Local Storage

Delete every localStorage entry in the current page context.

Parameterssession_idrequired

browser_new_tab

Open New Tab

Open a new browser tab. Returns a tab_id to use with browser_switch_tab.

Parametersurlsession_idrequired

browser_switch_tab

Switch Tab

Switch focus to a different browser tab.

Parameterstab_idrequiredsession_idrequired

browser_batch

Run Batch of Browser Actions

Execute a sequence of browser actions in a single call against an existing session. Use this when you already know the full sequence of steps — it reduces round trips and is faster than calling each tool individually. Actions run sequentially and stop at the first failure unless stop_on_error is false. Each step result is returned in the results array at the same index. Note: screenshots in batch results are returned as base64 strings inside the JSON; browser_screenshot returns them as images. A batch can include evaluate, so it can change or delete page state the same way browser_evaluate can.

Parametersactionsrequiredsession_idrequiredstop_on_error

Version history

20
  1. v2.19.0LatestNov 7, 2026
  2. v2.18.0Oct 30, 2026
  3. v2.17.0Oct 29, 2026
  4. v2.16.1Oct 25, 2026
  5. v2.16.0Oct 23, 2026
  6. v2.15.1Oct 21, 2026
  7. v2.15.0Oct 20, 2026
  8. v2.12.1Oct 19, 2026
  9. v2.12.0Oct 18, 2026
  10. v2.11.1Oct 17, 2026
  11. v2.10.2Oct 16, 2026
  12. v2.10.0Oct 16, 2026
  13. v2.9.1Oct 16, 2026
  14. v2.7.0Oct 14, 2026
  15. v2.6.1Oct 13, 2026
  16. v2.5.2Oct 12, 2026
  17. v2.5.1Oct 7, 2026
  18. v2.4.0Oct 6, 2026
  19. v2.3.0Oct 2, 2026
  20. v2.2.3Sep 16, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts