Skip to main content
Give a Vercel AI SDK agent live access to the protected web. The @zenrows/ai-sdk-tools package exposes Zenrows as three ready-made AI SDK tools, so a model can read a page, pull named fields out of it, or look at how it renders, without you writing a tool definition or a fetch wrapper. This guide covers installing the package, wiring it into generateText or streamText, and controlling what each call costs.

What is the Vercel AI SDK?

The Vercel AI SDK is a TypeScript toolkit for building applications with large language models. It gives you one interface across model providers, and it runs the tool-calling loop for you: you hand it a set of tools, and it decides when to call them, feeds the results back to the model, and keeps going until the model has an answer. A tool in the AI SDK is a description the model can read, a schema for its arguments, and a function that runs when the model calls it. @zenrows/ai-sdk-tools ships three of them already written.

Key benefits of integrating the Vercel AI SDK with Zenrows

  • No tool definitions to write: one factory call returns three tools with descriptions and schemas the model already understands.
  • Access to the protected web: pages behind antibot systems, JavaScript rendering, or geographic restrictions come back as ordinary text.
  • Output a model can read: pages arrive as Markdown rather than raw HTML, so you spend context on content rather than markup.
  • Fewer tokens when you know what you want: extractData returns named fields instead of a whole page.
  • Vision when layout is the question: takeScreenshot hands a real image part to the model, not a base64 string.
  • Spend visible per call: every result carries what the request cost, in credits and in US dollars.

Use cases

  • Research agents: let a model read several live sources and answer from them, rather than from training data.
  • Competitive monitoring: pull prices, titles, or availability from pages that block ordinary requests.
  • Retrieval over the live web: fetch and ground answers in pages that are too fresh to be indexed.
  • Layout and rendering questions: ask a vision model what a page looks like, not just what it says.
  • Lead and catalog enrichment: turn a URL into named fields a downstream step can consume.

Prerequisites

  • Node.js 22 or later. The package declares "node": ">=22".
  • A Zenrows API key, from the API keys page in your dashboard.
  • The AI SDK and Zod, which are peer dependencies: ai ^7.0.0 and zod ^3.25.76 || ^4.1.8.
  • A model provider, for example @ai-sdk/anthropic or @ai-sdk/openai, with its own API key.

Getting started: basic usage

Install the package:
Call createZenrowsTools with your API key and pass the result straight to generateText as its tools:
TypeScript
The model calls scrapeUrl on its own, reads the page, and answers:
Output
stopWhen is what lets the model take more than one turn. Without it the SDK stops after the first step, so the model calls a tool and never gets to use the result. isStepCount(3) gives it room to call a tool, read the output, and answer.
The same zenrows object works unchanged with streamText and with the SDK’s Agent class.

The three tools

createZenrowsTools returns an object with exactly these keys, so you can pass all three or hand the model a subset.

scrapeUrl

Returns a whole page as Markdown, plain text, or HTML. Use it when the model needs to read something and you do not know in advance which parts matter. Returns { content, truncated, url, finalUrl, creditsSpent, costUsd }.
TypeScript
Output

extractData

Returns named fields as JSON instead of a whole page. Far cheaper in context than reading the page and asking the model to find the fields itself. Returns { data, url, creditsSpent, costUsd }, plus note when Zenrows has something to say about the result.
TypeScript
Output
cssSelectors returns one array per field, in page order, not an array of paired objects. Pairing the first title with the first price is the model’s job, or yours. Fields that match a different number of elements will not line up.
Pass outputs instead of cssSelectors to collect one kind of thing across the whole page. Accepted values are emails, phone_numbers, headings, images, audios, videos, links, menus, hashtags, metadata, tables, and favicon.
TypeScript
With neither option, Zenrows decides what the page is about and extracts accordingly.

takeScreenshot

Captures the rendered page and hands the model a real image part through the AI SDK’s toModelOutput, so a vision-capable model sees the page rather than a base64 string. Your own code gets the image as base64 in { image, mediaType, bytes }.
TypeScript
Output
Use it for questions about layout, placement, or visual state. For questions about content, scrapeUrl is cheaper in both credits and tokens.

Configuration options

These are set once on the factory and are never exposed to the model, so it cannot change them mid-run.

On adaptive stealth

Zenrows can decide per request whether a page needs JavaScript rendering or a premium proxy, and bills for the configuration that worked. That is on by default, which is why the model is given no jsRender or premiumProxy knob to guess at. Zenrows rejects adaptive stealth combined with an explicit tier flag, so an option that needs a browser, such as a screenshot or a country-specific proxy, drops it for that one call and sets what the option actually needs instead. The model cannot produce a request Zenrows will refuse. Set adaptiveStealth: false if you would rather pay a flat, predictable cost per request and accept that protected pages will fail.

On content length

A scraped page goes into the model’s context and you pay for every character of it. HTML routinely runs past 80,000 characters for an ordinary page, so scrapeUrl truncates at maxContentLength and sets truncated: true rather than quietly sending a large bill. Prefer extractData when you already know which fields you want.

Cost and credits

Zenrows bills per request. Every tool result carries creditsSpent, the credits the request used, read from the X-Request-Credits response header, and costUsd, its cost in US dollars, from X-Request-Cost. A model can therefore report what a piece of research cost. Protected pages cost more, because adaptive stealth bills for the configuration that succeeded. See Pricing and credits for the full model. Two cases are not charged at all:
  • Extract recognises the page but finds no data on it. Zenrows answers 422 no_data_extracted. extractData returns that as an error telling the model to read the page with scrapeUrl instead of repeating the call.
  • Extract refuses the page. Zenrows answers 403 REQS007, also not charged. It covers three different situations: the domain is not prepared, the domain is prepared but Extract did not recognise this particular page, or Extract is not enabled for the site. See REQS007.
The tools return { error } rather than throwing. A failed call costs the agent a step, not the whole run, and the message is written for a model to act on.

Troubleshooting

The model calls a tool and then stops

Set stopWhen. Without it the AI SDK stops after the first step, so the model makes the tool call and never sees the result. isStepCount(3) is enough for read, think, answer.

The run uses far more tokens than expected

A whole page is going into context. Switch the call to extractData with cssSelectors, or lower maxContentLength so scrapeUrl truncates sooner. Check truncated on the result to see whether it is already happening.

extractData returns an error saying no data was extracted

Zenrows recognised the page but none of the requested fields came back filled. Check the selectors against the live page, or read it with scrapeUrl instead. The request was not charged.

extractData returns the whole page instead of named fields

The option is cssSelectors, not selectors. An unrecognised key is dropped by the schema and the call runs with no options, which makes Zenrows decide what to extract.

A tool reports an invalid API key

Confirm ZENROWS_API_KEY is set in the environment the code actually runs in, and that the key is enabled on the API keys page. Note that 403 REQS007 is about Extract refusing the page, not about the key.

extractData returns a 403 REQS007

The message you get back says Extract is not prepared for the URL. Read that as “Extract refused this page”, because REQS007 covers three situations and the message does not yet tell you which one you hit. Do this first, because it works in all three cases: call extractData again with cssSelectors or outputs, which need no preparation, or read the page with scrapeUrl. The request was not charged either way. Preparing the domain only helps one of the three. If the domain is genuinely unprepared, prepare it and retry. If Extract simply did not recognise this page, which is common on home and index pages, the domain is already prepared and preparing it again returns the same ready record and the same error. If Extract is not enabled for the site, preparing does nothing either. REQS007 tells the three apart by the title the API returns. Note that the AI SDK tools do not surface title, so to distinguish the cases you need to call the API directly.

Screenshots are expensive

A screenshot costs five credits against one for a scrape, because it needs a browser. Use it only for questions about layout. For content, scrapeUrl answers the same question for a fifth of the price.

Helpful resources

Frequently asked questions

All of them. The tools are plain AI SDK tools, so they work with any provider the SDK supports. Tool calling has to be supported by the model you choose.
Yes. createZenrowsTools returns an object keyed by scrapeUrl, extractData, and takeScreenshot. Pass only the keys you want as tools.
Yes. The same object passed as tools works unchanged in generateText, streamText, and the SDK’s Agent.
No. Adaptive stealth is on by default and decides per request, which is why the model is given no rendering options to guess at. Turn it off with adaptiveStealth: false if you want a flat cost per request.
Every tool result carries creditsSpent and costUsd. Sum them across the steps of a run, or let the model report them.
Each CSS selector is matched across the whole page, so each field comes back as an array in page order. Pairing them is left to you or the model, because fields do not always match the same number of elements.
The AI SDK can connect to MCP servers, but this package is the supported route for the AI SDK and needs no server connection. See the MCP overview for what the MCP server offers.
Yes. The package ships both ESM and CommonJS builds with TypeScript types.